Dynamic Programming学习笔记 (21) - 最长公共子序列 (力扣# 1143)

129 阅读2分钟

最长公共子序列问题与最长公共子串问题大致相同,不同之处在于对于与公共子序列而言,允许从原字串中删除某些字符,其题面为

给定两个字符串 text1 和 text2,其长度分别为N1和N2, 返回这两个字符串的最长公共子序列的长度。如果不存在公共子序列,返回 0 。 一个字符串的子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 两个字符串的公共子序列是这两个字符串所共同拥有的子序列。

实例如下:

text1 = "abcde", text2 = "ace" 
答案是3,最长公共子序列是"ace"。      

解题思路:

我们使用F(i, j)作为DP表达式,其中i和j分别是两个字符串的数组下标,F(i, j)返回的是text1[i:N1]和text2[j:N2]之间的最长公共子序列长度。F(i, j)的返回值有两种情况,当text1[i]和text2[j]相等时,F(i, j)的返回值等于1+F(i + 1, j + 1),而当text1[i]和text2[j]不等时,F(i, j)的返回值是F(i + 1, j) + 1和F(i, j + 1) + 1中的较大值,前者代表的是从text1中删除第i个字符后的最长公共子序列长度,而后者代表的是从从text2中删除第j个字符。

从以上表达式出发,我们使用一个二维的DP数组,以及双重循环,对text1和text2的数组下标分别按从大到小的顺序依次计算DP数组中的各个元素的值,最后DP[0][0]中的数值就是问题的答案。

Java代码如下

class Solution {
    public int longestCommonSubsequence(String s1, String s2) {
        char[] chars1 = s1.toCharArray();
        int N1 = chars1.length;

        char[] chars2 = s2.toCharArray();
        int N2 = chars2.length;

        int[][] dp = new int[N1 + 1][N2 + 1];

        for (int i = N1 - 1; i >= 0; i --) {
            for (int j = N2 - 1; j >= 0; j --) {
                if (chars1[i] == chars2[j]) {
                    dp[i][j] = 1 + dp[i + 1][j + 1];
                } else {
                    dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
                }
            }
        }
        return dp[0][0];
    }
}