LCS-最长公共子序列
其实有两个概念容易模糊:
1:最长公共字串
2:最长公共子序列
最长公共子串其实是连续的字符串,而最长公共子序列只要顺序不发生改变就可以。比如abcde, hbcxdo这样两个字符串
最长公共子串是:bc
最长公共子序列是:bcd
想法:
我们可以通过动态规划的思路来思考
假设字符串Xn, Ym 然后最长公共子序列是 Zk
(n, m, k代表字符串长度)
Xn:{x1, x2, x3……xn}
Ym: {y1, y2, y3……ym}
Zk:{z1, z2, z3……zk}
LCS(Xn, Ym)= Zk
当X, Y字符串最后一个字符相等(xn==ym),那么必定符合(zk==(xn , ym))。所以我们可以简化为LCS(Xn-1, Ym-1)=Zk-1
如果xn != ym,那么Zk等于LCS(Xn-1, Ym)或者LCS(Xn, Ym-1)
实现:
使用一个二维数组dp[i][j]来记录LCS(Xi, Yj)的长度
举例:
abcde 和 hbcxdo
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
3 | 0 | 0 | 1 | 2 | 2 | 2 | 2 |
4 | 0 | 0 | 1 | 2 | 2 | 3 | 3 |
5 | 0 | 0 | 1 | 2 | 2 | 3 | 3 |
代码实现:
int LCS(char s[]){
int len = strlen(s);
memset (dp, 0, sizeof(dp));
for (int i = 1; i <= len; ++ i){
for (int j = 1; j <= len; ++ j){
if (s[i - 1] == s[len - j]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[len][len];
}