博客 > 数据结构&算法 > 题目&比赛
# HDU 1156(POJ 1458) LCS [HDU-1159](http://acm.hdu.edu.cn/showproblem.php?pid=1159) [POJ-1458](http://poj.org/problem?id=1458) ## 题目分析 经典的LCS问题. ## 情况1: 仅需要输出LCS长度 ### 转移方程 $$ \begin{align} dp[i][j] = \begin{cases} 0, &i = 0 || j = 0\\ dp[i-1][j-1] + 1, &str1[i-1] = str2[j-1]\\ max(dp[i-1][j], dp[i][j-1]), &str1[i-1] \ne str2[j-1]\\ \end{cases} \end{align} $$ ### 代码(C++) 其实这里也可以用滚动数组. 会节省很多空间 ```c++ #include <iostream> #include <string> #include <algorithm> using namespace std; int main() { string str1, str2; while(cin >> str1 >> str2) { short LCS[1010][1010] = {0}; for(int i = 1; i <= str1.size(); i++) for(int j = 1; j <= str2.size(); j++) if(str1[i-1] == str2[j-1]) LCS[i][j] = LCS[i-1][j-1] + 1; else LCS[i][j] = max(LCS[i][j-1], LCS[i-1][j]); cout << LCS[str1.size()][str2.size()] << endl; } return 0; } ``` ## 情况2: 要求输出一个子序列 只要理解了LCS转移方程的本质, 就也很简单, 只要从$dp[n][m]$开始, 遇到相同字符回溯左上角(当前字符为LCS内字符), 不同字符回溯上或左(必然有至少一个状态与当前状态LCS长度相等), 最终就会到达数组边界(也即LCS长度为0), 找到一条LCS的逆序列. ### 代码(C++) ```c++ #include <algorithm> #include <string> #include <iostream> #include <cstring> using namespace std; int main() { string str1, str2; short *tdp = new short[2048 * 2048]; #define dp(x,y) *(tdp+(x)*(str2.size()+1)+(y)) while(cin >> str1 >> str2) { memset(tdp, 0, 2048 * 2048 * sizeof(short)); for (int i = 1; i <= str1.size(); ++i) { for (int j = 1; j <= str2.size(); ++j) { if (str1[i-1] == str2[j-1]) dp(i, j) = dp(i-1, j-1) + 1; else dp(i, j) = max(dp(i, j-1), dp(i-1, j)); } } } int x = str1.size(), y = str2.size(); string res; while (x >= 1 && y >= 1) { if (str1[x-1] == str2[y-1]) { res += str1[x-1]; --x, --y; } else if (dp(x, y) == dp(x-1, y)) --x; else --y; } reverse(res.begin(), res.end()); cout << res << endl; } ```