问题
在字符串S中按照先后顺序依次取出若干字符(不必连续),并将他们排列成一个新的字符串,这个字符串称为原字符串的子串。现在给定两个字符串S1和S2,求一个最长公共子串和其长度。
分析
- 最基础的方法就是枚举所有子序列,然后逐一判断子序列是否相同,时间复杂度
O
(
n
n
+
m
)
O(n^{n+m})
O(nn+m)
- 使用动态规划可将时间复杂度降为
O
(
n
m
)
O(nm)
O(nm)
- 设置二维数组
dp[][] ,令 dp[i][j] 表示以S1[i] 作为结尾和以S2[j] 为结尾的最长子序列的长度,最长为dp[n][m] - 当
S1[i]=S2[j] ,此时必定存在一个最长公共子序列以这个字符为结尾,其他部分等价于S1 中前i-1 个字符和S2 中前j-1 个字符中的最长公共子串,也就是dp[i][j]=dp[i-1][j-1]+1 - 当
S1[i]!=S2[j] ,则此时最长公共子串为S1 中前i-1 个字符和S2 中前j-1 个字符中的较大的最长公共子串,即dp[i][j]=max{dp[i-1][j],dp[i][j-1]} - 对于边界条件,如果两个字符串中有一个为空串,那么公共字符串的长度必然为0,即
dp[i][0]=dp[0][j]=0
例题
描述 寻找两个字符出的最长子串 输入描述: 输入的两行字符串有小写英文字母构成,且最大长度不会超过100 输出描述: 对于每个例子输出k表示最长公共子串的长度
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#define MAXN 101
using namespace std;
int dp[MAXN][MAXN];
string s1, s2;
int LCS(){
int n = s1.size(), m = s2.size();
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
if (s1[i - 1] == s2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[n][m];
}
int main(int argc, char const *argv[])
{
while (cin >> s1 >> s2){
int k = 0;
k = LCS();
printf("%d\n", k);
}
return 0;
}
|