【經典問題】二維動態規劃問題:求最長公共子序列LCS
原博地址:http://blog.csdn.net/yysdsyl/article/details/4226630
http://blog.csdn.net/ljyljyok/article/details/77905681
證明: http://blog.csdn.net/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.csdn.net/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());