原博地址:http://blog.youkuaiyun.com/yysdsyl/article/details/4226630
http://blog.youkuaiyun.com/ljyljyok/article/details/77905681
证明: http://blog.youkuaiyun.com/waltonhuang/article/details/52032463
最长公共子序列问题是一个经典的计算机科学问题,也是数据比较程序,比如Diff工具,和生物信息学应用的基础。它也被广泛地应用在版本控制,比如Git用来调和文件之间的改变。
对于任意数量的序列的LCS问题属于NP-hard,但对于两个已知长度(m、n)的序列,可以用动态规划的思想在多项式时间内解决。
假设两个字符串一个为A[n],另一个为B[m],引入二维数组c[ ][ ],用c[ i ][ j ]记录A[ i ]与A[ j ]的LCS长度,那么c[ i ][ j ]的递推公式为:
if(i==0||j==0)
c[i][j] = 0; //边界初始化
else if(A[i]==B[j]) //子串的最后一个字符相等
c[i][j] = c[i-1][j-1] + 1; //LCS的长度必然要比去掉这个相等的字符时的子串的LCS长度大1
else //子串的最后一个字符不相等
c[i][j] = max( c[i-1][j] , c[i][j-1] ); //LCS的长度必然 大于等于 去掉A中最后一个字符或去掉B中最后一个字符时的子串的LCS的长度
这个状态转移方程的证明如下:(转载自http://blog.youkuaiyun.com/waltonhuang/article/details/52032463)
当
A[i] == B[j]
时
显然,由于两个字符串的最后一位相同,S[i][j]
的长度应该比S[i-1][j-1]
大1.当
A[i] != B[j]
时
结论是:c[i][j] = max(c[i-1][j], c[i][j-1])
证明:
- 显然,由于添加了一个字符,
c[i][j]
肯定大于等于c[i-1][j]
,也肯定大于等于c[i][j-1]
。 但是,
c[i][j]
不能同时大于c[i-1][j]
和c[i][j-1]
。
反证法:
1.1. 如果c[i][j] > c[i-1][j]
,说明S[i][j]
的最后一位是A[i]
。(因为多了A[ i ]这个字符之后,最长公共子序列的长度多了1,所以多出来的这一位就是A[ i ]!)
1.2. 同理,如果c[i][j] > c[i][j-1]
,说明S[i][j]
的最后一位是B[j]
。
1.3. 那么如果c[i][j]
同时大于c[i-1][j]
和c[i][j-1]
,说明S[i][j]
的最后一位既是A[i]
,也是B[j]
。那么A[i]
就与B[j]
相同了!矛盾了!所以反证出:c[i][j]
不能同时大于c[i-1][j]
和c[i][j-1]。
- 显然,由于添加了一个字符,
然后排表如下图所示:(计算顺序是从i=1到i=7,以及j=1到j=6的)
可以看到,最右下角的c[7][6] = 4就是要求的LCS的值了。
代码如下:
class LCS {
public:
int findLCS(string A, int n, string B, int m) {
// write code here
int c[n+1][m+1];
int i,j;
memset(c, 0, sizeof(c));
for(i=1;i<=n;++i){ // i=0或j=0默认初始化为0
for(j=1;j<=m;++j){
if(A[i-1]==B[j-1])
c[i][j] = c[i-1][j-1] + 1;
else
c[i][j] = max(c[i-1][j],c[i][j-1]);
}
}
return c[n][m];
}
};
如果要求输出这个最长公共子序列,则需要通过数组c进行回溯:
string lcstr;
i = n; //将i、j下标落到数组c的末尾元素上。
j = m;
while (i != 0 && j != 0)
{
if (A[i] == B[j]) { //将相同的子元素压栈。然后指针前移,直到i、j指向0终止(因为任何字符串 与0 求公共子序列,都是0)
lcstr.push_back(A[i]);
--i;
--j;
}
else { //若二者不相等,而最长公共子序列一定是由LCS(c[i][j-1] or c[i-1][j])的较大者得来,故将较大者的指针前移,接着遍历。
if (c[i][j - 1] > c[i - 1][j]) {
--j; //将当前列前移到j-1列
}
else { // if(c[i][j - 1] <= c[i - 1][j])
--i;
}
}
}
//求LCS(之一)
reverse(lcstr.begin(), lcstr.end());