之前一直不懂LCS問題,然而找工做被LCS折磨的,與那個公司無緣了。ios
最長公共子序列問題是一道經典的動態規劃問題,最長公共子序列問題也有最優子結構。spa
即:Xi<x1, x2,..., xi>即X序列的前i個字符(1<=i<=m),Yj<y1, y2,..., yj)即Y序列的前j個序列(1<=j<=n);假設Z<z1,z2,...,Zk>屬於LCS(X,Y);code
若:xm==yn(最後一個字符相同),則不難用反證法證實:該字符必是X與Y的任一最長公共子序列Z(設長度爲k)的最後一個字符,即有zk = xm = yn 且顯然有Zk-1∈LCS(Xm-1 , Yn-1)即Z的前綴Zk-1是Xm-1與Yn-1的最長公共子序列。此時,問題化歸成求Xm-1與Yn-1的LCS(LCS(X , Y)的長度等於LCS(Xm-1 , Yn-1)的長度加1)。blog
若:xm≠yn,則亦不難用反證法證實:要麼Z∈LCS(Xm-1, Y),要麼Z∈LCS(X , Yn-1)。因爲zk≠xm與zk≠yn其中至少有一個必成立,若zk≠xm則有Z∈LCS(Xm-1 , Y),相似的,若zk≠yn 則有Z∈LCS(X , Yn-1)。此時,問題化歸成求Xm-1與Y的LCS及X與Yn-1的LCS。LCS(X , Y)的長度爲:max{LCS(Xm-1 , Y)的長度, LCS(X , Yn-1)的長度}。遞歸
因爲上述當xm≠yn的狀況中,求LCS(Xm-1 , Y)的長度與LCS(X , Yn-1)的長度,這兩個問題不是相互獨立的:二者都須要求LCS(Xm-1,Yn-1)的長度。另外兩個序列的LCS中包含了兩個序列的前綴的LCS,故問題具備最優子結構性質考慮用動態規劃法。string
也就是說,解決這個LCS問題,你要求三個方面的東西:一、LCS(Xm-1,Yn-1)+1;二、LCS(Xm-1,Y),LCS(X,Yn-1);三、max{LCS(Xm-1,Y),LCS(X,Yn-1)}。it
由上述能夠得出遞歸公式:io
#include <iostream> #include <vector> #include <cstring> using namespace std; enum decreaseDir {kInit = 0, kLeft, kUp, kLeftUp}; string ret; void LCS_Print(vector<vector<int> > &dir, const char *str1, const char *str2, int row, int col) { if(str1 == NULL || str2 == NULL) return; int len1 = strlen(str1); int len2 = strlen(str2); if(len1 == 0 || len2 == 0 || !(row < len1 && col < len2 )) return; if(dir[row][col] == kLeftUp) { if(row > 0 && col > 0) LCS_Print(dir, str1, str2, row - 1, col -1); ret.push_back(str1[row]); } else if(dir[row][col] == kUp) { if(row > 0) LCS_Print(dir, str1, str2, row - 1, col); } else if(dir[row][col] == kLeft) { if(col > 0) LCS_Print(dir, str1, str2, row, col -1); } } int LCS(const char *str1, const char *str2) { if(!str1 || !str2) return 0; int len1 = strlen(str1); int len2 = strlen(str2); if(!len1 || !len2) return 0; int i, j; vector<vector<int> > dp(len1, vector<int>(len2, 0)); vector<vector<int> > dir(len1, vector<int>(len2, 0)); for(int i = 0; i < len1; i++) { for(int j = 0; j < len2; j++) { if(i == 0 || j == 0) { if(str1[i] == str2[j]) { dp[i][j] = 1; dir[i][j] = kLeftUp; } else { if(i > 0) { dp[i][j] = dp[i-1][j]; dir[i][j] = kUp; } if(j > 0) { dp[i][j] = dp[i][j-1]; dir[i][j] = kLeft; } } } else if(str1[i] == str2[j]) { dp[i][j] = dp[i-1][j-1] + 1; dir[i][j] = kLeftUp; } else if(dp[i-1][j] > dp[i][j-1]) { dp[i][j] = dp[i-1][j]; dir[i][j] = kUp; } else { dp[i][j] = dp[i][j-1]; dir[i][j] = kLeft; } } } LCS_Print(dir, str1, str2, len1 - 1, len2 - 1); return dp[len1-1][len2-1]; } int main() { const char *str1 = "abcde"; const char *str2 = "acde"; ret.clear(); int longest = LCS(str1, str2); cout << "longest = " << longest << endl; cout << "ret = " << ret << endl; return 0; }