暴力和动态规划均可做
主要是了解动态规划怎么做;
我们发现,在相同开端的子串的比较中,有很多事重复动作。比如在比较以i,j分别为起点的子串时,有可能会进行i+1和j+1以及i+2和j+2位置的字符的比较。而以i+1,j+1分别为起点的子串时,这些字符又被比较了一次。也就说该问题有非常相似的子问题,而子问题之间又有重叠,这就给动态规划法创造了契机。
暴力解法是以子串开端开始寻找,现在换个思路,以相同子串的字符结尾来利用动态规划法。
假设两个字符串分别为A、B,A[i]和B[j]分别表示其第i和j个字符,再假设K[i,j]表示以A[i]和B[j]结尾的子串的最大长度。那么A,B分别再向下走一个字符,我们可以推断出K[i+1,j+1]与K[i,j]之间的关系,如果A[i] == B[j],那么K[i+1,j+1] = K[i,j] + 1;否则K[i+1,j+1] =0。而如果A[i+1]和B[j+1]相同,那么就只要在以A[i]和B[j]结尾的最长相同子串之后分别添上这两个字符即可,这样就可以让长度增加一位,综上所述,就是K[i+1,j+1] = (A[i] == B[j] ? K[i,j] + 1 : 0)的关系。
由上述K[i+1,j+1] = (A[i] == B[j] ? K[i,j] + 1 : 0)的关系,想到了使用二维数组来存储两个字符串之间的相同子串关系,因为K[i+1,j+1] = (A[i+1] == B[j+1] ? K[i,j] + 1 : 0)关系,只计算二维数据的最上列和最左列数值即可,其他数值通过K[i+1,j+1] = (A[i+1] == B[j+1] ? K[i,j] + 1 : 0)可得。
动态规划解法:
#include<bits/stdc++.h>
using namespace std;
int dp[1005][1005];
int main()
{
string s1,s2;
while(cin >> s1 >> s2){
int len1 = s1.size();
int len2 = s2.size();
if(len1 > len2){
swap(s1,s2);
swap(len1,len2);
}
memset(dp,0,sizeof(dp));
int MAX = 0;
for(int i = 0;i < len1;++i){
dp[i][0] = (s1[i] == s2[0]) ? 1 : 0;
}
for(int j = 1;j < len2;++j){
dp[0][j] = (s1[0] == s2[j]) ? 1 : 0;
}
int start = 0;
for(int i = 1;i < len1;++i){
for(int j = 1;j < len2;++j){
if(s1[i] == s2[j]){
dp[i][j] = dp[i - 1][j - 1] + 1;
}
if(dp[i][j] > MAX){
MAX = dp[i][j];
start = i - MAX + 1;
}
}
}
cout << s1.substr(start,MAX) << endl;
}
return 0;
}
暴力解法:
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s1,s2;
while(cin >> s1 >> s2){
int len1 = s1.size();
int len2 = s2.size();
if(len1 > len2){
swap(s1,s2);
swap(len1,len2);
}
int MAX = 0;
string ans;
for(int i = 0;i < len1;++i){
for(int j = 0;j < len2;++j){
if(s1[i] == s2[j]){
int cnt = 1;
int ii = i,jj = j;
while(i + 1 < len1 && j + 1 < len2 && s1[++ii] == s2[++jj]){
cnt++;
}
if(MAX < cnt){
MAX = cnt;
ans = s1.substr(i,cnt);
}
}
}
}
cout << ans << endl;
}
return 0;
}