本题是一类经典的二维动态规划问题。设 dp[i][j] 为 text1[0…i] 与 text2[0…j] 的最长公共子序列,那么本问题的状态转移方程为:
if(text1[i] == text2[j]) { dp[i][j] = 1 + dp[i-1][j-1]; //末尾字符相等时 } else { dp[i][j] = max(dp[i-1][j], dp[i][j-1]); //末尾字符不相等时 }
推算出状态转移方程后,写出代码求解就十分容易了。
方法一:递归(超时)
class Solution { public: int longestCommonSubsequence(string text1, string text2) { return ans(text1, text2, text1.length() - 1, text2.length() - 1); } int ans(string &text1, string &text2, int i, int j) { //i、j分别表示对text1、text2从下标0到i、j闭区间范围的字串进行求解 if(i < 0 || j < 0) return 0; else { if(text1[i] == text2[j]) return ans(text1, text2, i - 1, j - 1) + 1; else return max(ans(text1, text2, i - 1, j), ans(text1, text2, i, j - 1)); } } };
方法二:记忆化搜索
class Solution { public: int longestCommonSubsequence(string text1, string text2) { vector< vector<int> > flag(text1.length(), vector<int>(text2.length(), -1)); //将递归的结果储存到该数组中 return ans(text1, text2, text1.length() - 1, text2.length() - 1, flag); } int ans(string &text1, string &text2, int i, int j, vector< vector<int> > &flag) { if(i < 0 || j < 0) return 0; else { if(flag[i][j] != -1) //若该结果已经先前被计算过,直接返回flag数组中对应索引的内容 return flag[i][j]; if(text1[i] == text2[j]) flag[i][j] = ans(text1, text2, i - 1, j - 1, flag) + 1; else flag[i][j] = max(ans(text1, text2, i - 1, j, flag), ans(text1, text2, i, j - 1, flag)); return flag[i][j]; } } };
方法三:动态规划
class Solution { public: int longestCommonSubsequence(string text1, string text2) { if(text1.length() == 0 || text2.length() == 0) return 0; int n = text1.length(); int m = text2.length(); vector< vector<int> > dp(n, vector<int>(m, -1)); dp[0][0] = (text1[0] == text2[0] ? 1 : 0); //初始化dp[0][0] for(int i = 1; i < n; i ++) { //初始化dp[1...n-1][0] dp[i][0] = (text1[i] == text2[0] ? 1 : dp[i - 1][0]); } for(int j = 1; j < m; j ++) { //初始化dp[0][1...m-1] dp[0][j] = (text1[0] == text2[j] ? 1 : dp[0][j - 1]); } for(int i = 1; i < n; i ++) { for(int j = 1; j < m; j ++) { if(text1[i] == text2[j]) dp[i][j] = 1 + dp[i - 1][j - 1]; else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); } } return dp[n - 1][m - 1]; } };