题目
类型:动态规划
解题思路
正确的思路是 不要考虑整个字符串,而是细化到s1和s2的每个字符。
对于两个字符串求子序列的问题,都是用两个指针i和j分别在两个字符串上移动,大概率是动态规划思路。
最长公共子序列的问题也可以遵循这个规律,我们可以先写一个dp函数:
// 定义:计算 s1[i..] 和 s2[j..] 的最长公共子序列长度int dp(String s1, int i, String s2, int j)
这个dp函数的定义是:dp(s1, i, s2, j)
计算s1[i..]和s2[j..]
的最长公共子序列长度。
根据这个定义,那么我们想要的答案就是dp(s1, 0, s2, 0)
,且 base case 就是i == len(s1)
或j == len(s2)
时,因为这时候s1[i..]或s2[j..]就相当于空串了,最长公共子序列的长度显然是 0
代码
class Solution {
public int longestCommonSubsequence(String s1, String s2) {
int m = s1.length(), n = s2.length();
int[][] dp = new int[m + 1][n + 1];
// 定义:s1[0..i-1] 和 s2[0..j-1] 的 lcs 长度为 dp[i][j]
// 目标:s1[0..m-1] 和 s2[0..n-1] 的 lcs 长度,即 dp[m][n]
// base case: dp[0][..] = dp[..][0] = 0
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// 现在 i 和 j 从 1 开始,所以要减一
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
// s1[i-1] 和 s2[j-1] 必然在 lcs 中
dp[i][j] = 1 + dp[i - 1][j - 1];
} else {
// s1[i-1] 和 s2[j-1] 至少有一个不在 lcs 中
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
}
}
}
return dp[m][n];
}
}