首页 > 其他分享 >Acwin-3692. 最长连续公共子序列——待续

Acwin-3692. 最长连续公共子序列——待续

时间:2024-07-14 20:51:43浏览次数:15  
标签:待续 string int s2 3692 mid Acwin length s1

1.题目

输入两个字符串 s1,s2

输出最长连续公共子串长度和最长连续公共子串。

输入格式
一行,两个字符串 s1,s2,用空格隔开。

输出格式
第一行输出最长连续公共子串的长度

第二行输出最长连续公共子串。如果不唯一,则输出 s1
中的最后一个。

数据范围
1≤|s1|,|s2|≤100

数据保证至少存在一个连续公共子串。

输入样例:
abcdefg qwercdefiok
解释
输出样例:
4
cdef

2.题解

2.1.1 暴力枚举

思路

注意strcmp用于比较char字符串(字符常量),strcmp中: 1. s1 > s2 返回1, 2. s1 == s2, 返回0, 3. s1 < s2,返回-1
比较两个字符串string类型,使用函数compare即可!

代码

#include<bits/stdc++.h>
using namespace std;

int main(){
	string s1, s2;
	cin >> s1 >> s2;
	int ans = 1;
	string s;
	int n = min(s1.length(), s2.length());
	for(int i = 1; i <= n; i++){
		bool found = false;
		for(int j = 0; j <= s1.length() - i; j++){
			for(int k = 0; k <= s2.length() - i; k++){
				string ss1 = s1.substr(j, i), ss2 = s2.substr(k, i);
//				if(ss1.compare(ss2) == 0){
				if(ss1 == ss2){
					ans = i;
					s = ss1;
				} 
			}
		} 
	}
	cout << ans << endl << s;
	return 0;
} 

复杂度

时间复杂度:\(O(n^4)\)

2.1.2 暴力枚举(优化)

思路

找的是最长的,最后一个出现的,所以倒过来枚举即可(从大到小 + 字符串逆序比较)

代码

#include<bits/stdc++.h>
using namespace std;

int main(){
	string s1, s2;
	cin >> s1 >> s2;
	int ans = 1;
	string s;
	int n = min(s1.length(), s2.length());
	for(int i = n; i >= 1; i--){
		for(int j = s1.length() - i; j >= 0; j--){
			for(int k = s2.length() - i; k >= 0; k--){
				string ss1 = s1.substr(j, i), ss2 = s2.substr(k, i);
				if(ss1 == ss2){
					ans = i;
					s = ss1;
				} 
			}
			if(s.size()) break;
		} 
		if(s.size()) break;
	}
	cout << ans << endl << s;
	return 0;
} 

2.2.1 二分查找

思路

这里使用左闭右开的二分查找方式,也就是 while(left < right){ ...left = mid + 1(满足条件) ... right = mid(不满足条件)}
这里每次更新搜索区间(不包含上次找到的target位置),每次满足条件都是向着长度更大的方向进行搜索

补充:如果想向着更小的方向寻找:
while(left < right){ ...right = mid - 1(满足条件) ... left = mid (不满足条件)}

代码

#include<bits/stdc++.h>
using namespace std;

int main(){
	string s1, s2;
	cin >> s1 >> s2;
	int ans = 1;
	string s;
	int n = min(s1.length(), s2.length());
	int left = -1, right = n - 1;
	while(left < right){
		bool found = false;
		int mid = (left + right) / 2;
		int len = mid + 1; // 长度和下标不一样 
		// 下标-下标 等价于 长度 - 长度(下标 + 1 - 下标 - 1) ,所以这里不需要额外处理
		for(int j = s1.length() - len; j >= 0 && !found;  j--){
			for(int k = s2.length() - len; k >= 0 && !found; k--){
				string ss1 = s1.substr(j, len), ss2 = s2.substr(k, len);
				if(ss1 == ss2){
					ans = len;
					s = ss1;
					found = true;
				}
			}
		} 
		if(found){
			left = mid + 1;
		}else{
			right = mid;
		}
	} 
	cout << ans << endl << s;
	return 0;
} 

代码二——左闭右闭

#include<bits/stdc++.h>
using namespace std;

int main(){
	string s1, s2;
	cin >> s1 >> s2;
	int ans = 1;
	string s;
	int n = min(s1.length(), s2.length());
	int left = 0, right = n; // 长度取值范围为 [0, n]
	while(left <= right){
		bool found = false;
		int mid = (left + right) / 2;
		int len = mid + 1; // 长度和下标不一样 
		// 下标-下标 等价于 长度 - 长度(下标 + 1 - 下标 - 1) ,所以这里不需要额外处理
		for(int j = s1.length() - len; j >= 0 && !found;  j--){
			for(int k = s2.length() - len; k >= 0 && !found; k--){
				string ss1 = s1.substr(j, len), ss2 = s2.substr(k, len);
				if(ss1 == ss2){
					ans = len;
					s = ss1;
					found = true;
				}
			}
		} 
		if(found){
			left = mid + 1;
		}else{
			right = mid - 1;
		}
	} 


	cout << ans << endl << s;
	return 0;
} 

复杂度

时间复杂度: \(O(nlog_n)\)

2.2.2 二分 + 字符串哈希(滚动哈希)

思路

代码

#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>

using namespace std;

class Solution {
public:
    pair<int, string> longestCommonSubstring(string s1, string s2) {
        int n1 = s1.length();
        int n2 = s2.length();

        int P = 131313; // 基数
        long long MOD = 1e9 + 7; // 余数

        // 计算 s1 和 s2 的哈希值和幂次方值
        vector<long long> h1(n1 + 1, 0), p1(n1 + 1, 1);
        vector<long long> h2(n2 + 1, 0), p2(n2 + 1, 1);

        for (int i = 1; i <= n1; ++i) {
            h1[i] = (h1[i - 1] * P + s1[i - 1]) % MOD;
            p1[i] = (p1[i - 1] * P) % MOD;
        }

        for (int i = 1; i <= n2; ++i) {
            h2[i] = (h2[i - 1] * P + s2[i - 1]) % MOD;
            p2[i] = (p2[i - 1] * P) % MOD;
        }
        
        // 哈希函数
        auto getHash = [&](const vector<long long> &h, const vector<long long> &p, int l, int r) {
            return (h[r + 1] - h[l] * p[r - l + 1] % MOD + MOD) % MOD; // 将在h[r + 1]中 h[l] 多乘的 p[r - l + 1] 的次数补上(这里下标对应次数)
        };

        // 二分查找最长公共子串长度
        int left = 0, right = min(n1, n2), maxLen = 0;
        string longestSubstr = "";
        // 左闭右闭形式
        while (left <= right) {
            int mid = left + (right - left) / 2;
            unordered_map<long long, vector<int>> hashTable;

            // 存储s1中所有长度为mid的子串的哈希值
            for (int i = 0; i + mid - 1 < n1; ++i) {
                long long hashValue = getHash(h1, p1, i, i + mid - 1);
                hashTable[hashValue].push_back(i);
            }

            bool found = false;
            // 倒序查找 s2 保证找到的是最后一个!
            for (int i = n2 - mid; i >= 0 && !found; --i) {
                long long hashValue = getHash(h2, p2, i, i + mid - 1);
                if (hashTable.find(hashValue) != hashTable.end()) {
                    // 如果在s2中找到相同的哈希值
                    for (int pos : hashTable[hashValue]) {
                        if (s1.substr(pos, mid) == s2.substr(i, mid)) {
                            found = true;
                            maxLen = mid;
                            longestSubstr = s1.substr(pos, mid);
                        }
                    }
                }
            }

            if (found) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        return {maxLen, longestSubstr};
    }
};

int main() {
    string s1, s2;
    cin >> s1 >> s2;

    Solution solution;
    pair<int, string> result = solution.longestCommonSubstring(s1, s2);

    cout << result.first << endl;
    cout << result.second << endl;

    return 0;
}

复杂度

时间复杂度 \(O ((n1 +n2)log(min(n1,n2)))\), 即\(O(nlogn)\)
1.哈希预处理
计算哈希值和幂次方值的时间复杂度为O(n1 + n2)

2.二分查找
二分查找的次数为 O(log(min(n1,n2)))

3.子串哈希值计算和匹配:
在每次二分查找中,对于每个中间长度 mid,我们需要遍历 O(n1+n2) 个子串,并进行哈希值的查找和比较。

2.3 后缀数组 + LCP

思路

代码



复杂度

时间复杂度: \(O(nlogn)\)

标签:待续,string,int,s2,3692,mid,Acwin,length,s1
From: https://www.cnblogs.com/trmbh12/p/18297213

相关文章

  • 线段树——AcWing 245. 你能回答这些问题吗
    目录线段树定义运用情况注意事项解题思路AcWing245.你能回答这些问题吗题目描述运行代码代码思路改进思路线段树定义线段树是一种用于区间查询和更新问题的数据结构。它通过递归地将一个区间分解为若干子区间,每个节点代表一个子区间的和、最小值、最大值等信息,......
  • 拓扑排序——AcWing 164. 可达性统计
    目录拓扑排序定义运用情况注意事项解题思路AcWing164.可达性统计题目描述运行代码代码思路改进思路拓扑排序定义拓扑排序(TopologicalSort)是对有向无环图(DirectedAcyclicGraph,简称DAG)的一种排序方式。在一个有向无环图中,拓扑排序的结果是一个线性的顶点序列,其......
  • 并查集——AcWing 239. 奇偶游戏
    目录并查集定义运用情况注意事项解题思路AcWing239.奇偶游戏题目描述运行代码代码思路改进思路并查集定义并查集(DisjointSetUnion,简称DSU),是一种树形的数据结构,常用于处理一些不交集的合并及查询问题。在并查集中,元素被分成多个不相交的集合,每个集合由一个代表......
  • 最近公共祖先——AcWing 356. 次小生成树
    最近公共祖先定义最近公共祖先(LowestCommonAncestor,LCA)是在一棵有根树中,对于两个节点u和v,LCA是所有公共祖先中深度最大的一个节点。换句话说,LCA是u 和v的共同祖先中距离根节点最远的一个。运用情况最短路径问题:在树中,求两节点间的最短路径,可以先找到它们的LCA,......
  • Floyd算法——AcWing 343. 排序
    目录Floyd算法定义运用情况注意事项解题思路基本步骤AcWing343.排序 题目描述运行代码代码思路改进思路Floyd算法定义Floyd算法,全称Floyd-Warshall算法,是一种用于解决图中所有顶点对之间的最短路径问题的动态规划算法。它适用于带权有向图,且可以处理负权重边(......
  • Acwing 5729.闯关游戏 状压DP
    Acwing5729.闯关游戏状压DP题目链接题意:现在进行一个闯关游戏,一共有\(n\)个关卡,第\(i\)个关卡的分数为\(w_i\)。另外还有\(k\)个联动彩蛋。如果玩家通过第\(x\)个关卡后,紧接着通过了第\(y\)个关卡,就可以获得额外\(c\)分。现在你需要恰好通过\(m\)个不同关卡,顺......
  • 最小步数模型——AcWing 1107. 魔板
    最小步数模型定义最小步数模型通常是指在某种约束条件下,寻找从初始状态到目标状态所需的最少操作或移动次数的问题。这类问题广泛存在于算法、图论、动态规划、组合优化等领域。具体来说,它涉及确定一个序列或路径,使得按照特定规则执行一系列步骤后,能够从起始位置或状态转换到......
  • 【AcWing】843. n皇后问题
    剪纸就是判断当前方案一定不合法了就不往下搜了,把这个枝减掉,直接回溯。代码#include<iostream>usingnamespacestd;constintN=10;intn;charg[N][N];//棋盘boolcol[N],dg[N*2],udg[N*2];//分别代表列斜线反斜线有没有占用,对角线个数为2n-1,开2nvoiddfs(......
  • 【AcWing】842. 排列数字(DFS)
    DFS是树形结构,深度优先搜索。dfs可以算作递归。横线上填和前面不一样的数字。每一次向下探索都一条路走到黑,直到不能走再回溯。#include<iostream>usingnamespacestd;constintN=10;intn;intpath[N];//存放走过的路径boolst[N];//存放1~n哪个数用过了,全局b......
  • 贪心推公式——AcWing 125. 耍杂技的牛
    贪心推公式定义贪心算法是一种在每一步选择中都采取在当前状态下最优的选择,希望通过局部的最优选择来得到全局最优解的算法策略。运用情况问题具有最优子结构,即一个问题的最优解包含其子问题的最优解。可以通过局部最优决策逐步推导到全局最优。问题的选择策略相对明确且易......