描述
查找两个字符串a,b中的最长公共子串。若有多个,输出在较短串中最先出现的那个。
注:子串的定义:将一个字符串删去前缀和后缀(也可以不删)形成的字符串。请和“子序列”的概念分开!
数据范围:字符串长度1<= length<=30
进阶:时间复杂度:O(n^3)
,空间复杂度:O(n)
示例
输入:
abcdefghijklmnop abcsafjklmnopqrstuvw
输出:
jklmnop
思路:
方法一:暴力搜索
我们把s1
设置为较短的字符串,然后截取子串在s2
中查找,维护最长的即可。
时机复杂度为:O(m^3n)
,因为枚举 s1
所有的子串需要O(m^2)
,find 函数查找 s2
中是否含有子串需要O(mn)
空复杂度:O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <bits/stdc++.h> using namespace std;
int main(){ string s1,s2; cin >> s1 >> s2; int n = s1.size(); int m = s2.size(); if(n > m){ swap(s1, s2); } string res = ""; for(int i = 0; i < s1.size(); i++){ for(int j = i; j < s1.size(); j++){ if(s2.find(s1.substr(i, j - i + 1)) == string::npos){ break; }else if(res.size() < j - i + 1){ res = s1.substr(i, j - i + 1); } } } cout << res << endl; return 0; }
|
方法二:优化
我们不需要像方法一一样完全枚举,我们可以遍历两个字符串的所有字符串作为起点,然后同时开始检查字符是否相等,相等则不断后移,增加子串长度,否则说明这个子串截止了,不需要再遍历了,后续比较维护最大值即可。
时机复杂度为:O(m^2n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <bits/stdc++.h> using namespace std;
int main(){ string s1,s2; cin >> s1 >> s2; int n = s1.size(); int m = s2.size(); if(n > m){ swap(s1, s2); } string res = ""; for(int i = 0; i < s1.size(); i++){ for(int j = 0; j < s2.size(); j++){ int len = 0; int x = i, y = j; while(x < s1.size() && y < s2.size() && s1[x] == s2[y]){ x++; y++; len++; } if(res.size() < len){ res = s1.substr(i, x - i); } } } cout << res << endl; return 0; }
|
方法三:动态规划
dp[i][j]
表示在 s1 中以 第 i 个字符结尾在 s2 中以第 j
个字符结尾时的公共子串长度,
遍历两个字符串dp数组,在这个过程中比较维护最大值即可。
转移方程为:如果遍历到的该位两个字符相等,则此时长度等于两个前一位长度+1,
dp[i][j]=dp[i−1][j−1]+1
,如果遍历到该位时两个字符不相等,则置为0,因为这是子串,必须连续相等,断开要重新开始。
时机复杂度为:O(mn)
空间复杂度:O(mn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <bits/stdc++.h> using namespace std;
int main(){ string s1,s2; cin >> s1 >> s2; int n = s1.size(); int m = s2.size(); if(n > m){ swap(s1, s2); } vector<vector<int> > dp(s1.size() + 1, vector<int>(s2.size() + 1, 0)); int max = 0, end = 0; for(int i = 1; i <= s1.size(); i++){ for(int j = 1; j <= s2.size(); j++){ if(s1[i - 1] == s2[j - 1]){ dp[i][j] = dp[i - 1][j - 1] + 1; }else{ dp[i][j] = 0; } if(dp[i][j] > max){ max = dp[i][j]; end = i - 1; } } } cout << s1.substr(end - max + 1, max) << endl; return 0; }
|