1143. 最长公共子序列

310 阅读1分钟

思路:动态规划

所谓子序列,就是要保留原始顺序,但可以是不连续的。审题之后你可能会有疑问,这个问题为啥就是动态规划来解决呢?因为子序列类型的问题,穷举出所有可能的结果都不容易,而动态规划算法做的就是穷举 + 剪枝,它俩天生一对儿。所以可以说只要涉及子序列问题,十有八九都需要动态规划来解决。

  • dp[i][j]的意思:dp[i][j]表示以str1(i-1)为结尾的s1的子序列 和 以str2(j-1) 为结尾的s2的子序列的公共子序列的最长长度
  • 如果s1[i] = s2[j]
  • 如果s1[i] != s2[j],需要继承下来之前的长度,max一下。
  • 最后返回数组最后一个元素即可,因为即使两个单词的最后一个字符不相同,他们还是会继承之前的更短的序列的最大长度,比如abcdex abcdek还是会返回3,这就是子序列的特点。

为了保证第一行和第一列的元素可以取到dp[i - 1][j - 1],在他们前面填充0

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length();
        int n = text2.length();
        int[][] dp = new int[m + 1][n + 1];//为了最左和顶部多出一行0,这里声明。
        //注意ij的取值范围
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                //填充0,声明数组默认就全填0
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {//如果两个char相等,则在s1、s2都去掉char的情况上+1
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);//不相等,看s1去掉char大还是s2去掉char大
                }
            }
        }
        return dp[m][n];
    }
}

 // 获取子序列
    public String getPath(int[][] dp, String text1, String text2) {
        StringBuffer sb = new StringBuffer();
        int i = dp.length - 1, j = dp[0].length - 1; 
        while (i >= 1 && j >= 1) {
            if (dp[i][j] == dp[i - 1][j - 1] + 1) { //char1 = char2
                sb.append(text1.charAt(i - 1));
                i--;
                j--;
            } else if (dp[i][j] == dp[i - 1][j]) {//char1 != char2
                i--;
            } else if (dp[i][j] == dp[i][j - 1]) {//char1 != char2
                j--;
            }
        }
        return sb.reverse().toString();
    }