题目

类型:动态规划

image.png

解题思路

正确的思路是 不要考虑整个字符串,而是细化到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

详解最长公共子序列问题,秒杀三道动态规划题目

代码

  1. class Solution {
  2. public int longestCommonSubsequence(String s1, String s2) {
  3. int m = s1.length(), n = s2.length();
  4. int[][] dp = new int[m + 1][n + 1];
  5. // 定义:s1[0..i-1] 和 s2[0..j-1] 的 lcs 长度为 dp[i][j]
  6. // 目标:s1[0..m-1] 和 s2[0..n-1] 的 lcs 长度,即 dp[m][n]
  7. // base case: dp[0][..] = dp[..][0] = 0
  8. for (int i = 1; i <= m; i++) {
  9. for (int j = 1; j <= n; j++) {
  10. // 现在 i 和 j 从 1 开始,所以要减一
  11. if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
  12. // s1[i-1] 和 s2[j-1] 必然在 lcs 中
  13. dp[i][j] = 1 + dp[i - 1][j - 1];
  14. } else {
  15. // s1[i-1] 和 s2[j-1] 至少有一个不在 lcs 中
  16. dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
  17. }
  18. }
  19. }
  20. return dp[m][n];
  21. }
  22. }