I have never understood the LCS problem before. However, the problem of finding a job being tortured by LCs is lost with that company.
The longest common subsequence problem is a classic dynamic programming problem. The longest common subsequence problem also has the optimal substructure.
That is: xi <x1, x2 ,..., xi> that is, the first I characters (1 <= I <= m), YJ <Y1, Y2 ,..., YJ) that is, the first J sequences of the Y sequence (1 <= j <= N); assume z <Z1, Z2 ,..., ZK> belongs to LCS (x, y );
If: XM = yn (the last character is the same), it is not difficult to prove that this character must be any of the longest common subsequences of X and y z (set length to K) the last character, that is, zk = XM = YN and apparently there is a Zk-1 in LCS (Xm-1, Yn-1) That is Z prefix Zk-1 is the longest common subsequence of Xm-1 and Yn-1. At this time, the question is to find the Xm-1 and Yn-1 LCS (The length of LCS (X, Y) is equal to that of LCS (Xm-1, Yn-1).
If: XM = YN, it is not difficult to use reverse proof: either Z, LCS (Xm-1, Y), or Z, LCS (x, Yn-1 ). Because ZK and ZK have at least one of them must be true, ZK and XM has Z, LCS (Xm-1, Y), similar, if ZK is not YN, there is Z in LCS (x, Yn-1 ). At this time, the question is to find the Xm-1 and Y LCs and X and Yn-1 LCS. The length of LCS (X, Y) is: the length of max {LCS (Xm-1, Y), the length of LCS (x, Yn-1 }.
Since the above when XM =yn, finding the length of LCS (Xm-1, Y) and LCS (x, Yn-1) are not mutually independent of each other: both require the length of LCS (Xm-1, Yn-1. The LCS of the other two sequences contains the LCS of the prefix of the two sequences. Therefore, the dynamic programming method is used for the problem of optimal sub-structure.
That is to say, to solve this LCS problem, you require three things: 1. LCS (Xm-1, Yn-1) + 1; 2. LCS (Xm-1, Y), LCS (X, yn-1); 3, max {LCS (Xm-1, Y), LCS (x, Yn-1 )}.
The recursive formula can be obtained from the above:
#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;}
Longest Common subsequence (LCS)