题目描述:最长公共子序列(力扣1143), 即LCS
1. 两个字符串的常用思路
用两个指针i
和 j
分别再字符串上移动
2. LCS分析:
str1和str2中必定有一个串的字符在LCS中,或者str1和str2都在LCS中
3. 解法:
法一:动态规划
- 明确dp数组含义:s1[0…i-1] 和 s2[0…j-1] 的lcs长度为dp[i][j],
/*
为什么要加1,原因是你可以不加1,
但是不加1你就会用其它限制条件来确保这个index是有效的,
而当你加1之后你就不需要去判断只是让索引为0的行和列表示空串。
*/
int[][] dp = new int[m+1][n+1];
- 代码
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length();
int n = text2.length();
// dp定义:s1[0...i-1] 和s2[0...j-1]的lcs长度为dp[i][j]
int[][] dp = new int[m + 1][n + 1];
// 注意从1开始,m结束,因为i,j 为0时是空串,即dp table表中的第一行第一列全部为空
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// 因为 i, j 现在是从1开始,所以下标开始是i-1
if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
//text1,和text2 都在LCS中,长度加一
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
// text1或text2 中至少有一个不再LCS中
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
法二:备忘录,去除重叠子问题
// 法二
int[][] memo;
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length();
int n = text2.length();
memo = new int[m][n];
for (int[] row : memo)
Arrays.fill(row, -1);// 备忘录初始化为-1,
return dp(text1, 0, text2, 0);// 从0开始,即空串开始查找,
}
public int dp(String str1, int i, String str2, int j) {
// base case,空串返回0,
if (str1.length() == i || str2.length() == j)
return 0;
// 如果之前已经算过了,那就返回,避免重复计算
if (memo[i][j] != -1)
return memo[i][j];
// 找最长
if (str1.charAt(i) == str2.charAt(j))// 都在lcs中,长度加一,str1,str2通知后移以为,因为最开始是从0开始的
memo[i][j] =1 + dp(str1, i + 1, str2, j + 1);
else // 至少有一个不在lcs中,str1和str2只有一个需要后移,且取最大的公共序列
memo[i][j] = Math.max(dp(str1, i + 1, str2, j), dp(str1, i, str2, j + 1));
return memo[i][j];
}