跳至主要內容

最长公共子序列(LCS)

大约 4 分钟

最长公共子序列(LCS)

题目难度
双串问题:最经典双串 LCS 系列
1143. 最长公共子序列open in new window中等
712. 两个字符串的最小 ASCII 删除和open in new window中等
718. 最长重复子数组open in new window中等
583. 两个字符串的删除操作open in new window中等同 1143
1035. 不相交的线open in new window中等同 1143
1092. 最短公共超序列open in new window困难同 1143
双串问题:字符串匹配系列
72. 编辑距离open in new window困难
DD-2019002. 英文单词拼写纠错推荐open in new window简单同 72
44. 通配符匹配open in new window困难
10. 正则表达式匹配open in new window困难
双串问题:其它双串 dp[i][j] 问题
97. 交错字符串open in new window中等
115. 不同的子序列open in new window困难
双串问题:带维度双串 dp[i][j][k]
87. 扰乱字符串open in new window困难

1143. 最长公共子序列

public class Solution1143 {
    public int longestCommonSubsequence(String text1, String text2) {
        int n = text1.length();
        int m = text2.length();

        // f[i][j] 表示 text1 前 i 个元素与 text2 前 j 个元素的最长公共子序列
        int[][] f = new int[n + 1][m + 1];
        for (int i = 1; i <= n; i++) {
            char c1 = text1.charAt(i - 1);
            for (int j = 1; j <= m; j++) {
                char c2 = text2.charAt(j - 1);
                if (c1 == c2) {
                    f[i][j] = f[i - 1][j - 1] + 1;
                } else {
                    f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]);
                }
            }
        }
        return f[n][m];
    }
}

712. 两个字符串的最小 ASCII 删除和

public class Solution712 {
    public int minimumDeleteSum(String s1, String s2) {
        int n = s1.length();
        int m = s2.length();

        // f[i][j] 表示 s1 前 i 个元素与 s2 前 j 个元素的最小ASCII删除和
        int[][] f = new int[n + 1][m + 1];

        // 初始状态
        for (int i = 1; i <= n; i++) {
            f[i][0] = f[i - 1][0] + s1.codePointAt(i - 1);
        }
        for (int j = 1; j <= m; j++) {
            f[0][j] = f[0][j - 1] + s2.codePointAt(j - 1);
        }

        // 状态转移
        for (int i = 1; i <= n; i++) {
            int code1 = s1.codePointAt(i - 1);
            for (int j = 1; j <= m; j++) {
                int code2 = s2.codePointAt(j - 1);
                if (code1 == code2) {
                    f[i][j] = f[i - 1][j - 1];
                } else {
                    f[i][j] = Math.min(f[i - 1][j] + code1, f[i][j - 1] + code2);
                }
            }
        }
        return f[n][m];
    }
}

718. 最长重复子数组

public class Solution718 {
    public int findLength(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int m = nums2.length;

        // f[i][j] 表示 nums1 前 i 个元素且以 i 结尾与 nums2 前 j 个元素且以 j 结尾的最长重复子数组
        int[][] f = new int[n + 1][m + 1];
        int max = 0;
        for (int i = 1; i <= n; i++) {
            int num1 = nums1[i - 1];
            for (int j = 1; j <= m; j++) {
                int num2 = nums2[j - 1];
                if (num1 == num2) {
                    f[i][j] = f[i - 1][j - 1] + 1;
                } else {
                    f[i][j] = 0;
                }
                max = Math.max(max, f[i][j]);
            }
        }
        return max;
    }
}

72. 编辑距离

public class Solution72 {
    public int minDistance(String word1, String word2) {
        int n = word1.length();
        int m = word2.length();

        // f[i][j] 表示 word1 前 i 个字母与 word2 前 j 个字母的编辑距离
        int[][] f = new int[n + 1][m + 1];

        // 初始状态
        for (int i = 1; i <= n; i++) {
            f[i][0] = i;
        }
        for (int j = 1; j <= m; j++) {
            f[0][j] = j;
        }

        // 状态转移
        for (int i = 1; i <= n; i++) {
            char c1 = word1.charAt(i - 1);
            for (int j = 1; j <= m; j++) {
                char c2 = word2.charAt(j - 1);
                if (c1 == c2) {
                    // 在 word1 插入,在 word2 插入,替换一个
                    f[i][j] = Math.min(f[i - 1][j] + 1, Math.min(f[i][j - 1] + 1, f[i - 1][j - 1]));
                } else {
                    f[i][j] = Math.min(f[i - 1][j] + 1, Math.min(f[i][j - 1] + 1, f[i - 1][j - 1] + 1));
                }
            }
        }
        return f[n][m];
    }
}

44. 通配符匹配

public class Solution44 {
    public boolean isMatch(String s, String p) {
        int n = s.length();
        int m = p.length();

        // f[i][j] 表示字符串 s 的前 i 个元素和字符串 p 的前 j 个元素是否能匹配
        boolean[][] f = new boolean[n + 1][m + 1];
        f[0][0] = true;
        for (int i = 1; i <= m; ++i) {
            if (p.charAt(i - 1) == '*') {
                f[0][i] = true;
            } else {
                break;
            }
        }
        // 状态转移
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (p.charAt(j - 1) == '*') {
                    f[i][j] = f[i][j - 1] || f[i - 1][j];
                } else if (p.charAt(j - 1) == '?' || s.charAt(i - 1) == p.charAt(j - 1)) {
                    f[i][j] = f[i - 1][j - 1];
                }
            }
        }
        return f[n][m];
    }
}

10. 正则表达式匹配

public class Solution10 {
    public boolean isMatch(String s, String p) {
        int n = s.length();
        int m = p.length();

        // f[i][j] 表示 s 的前 i 个元素与 p 的前 j 个元素是否能够匹配
        boolean[][] f = new boolean[n + 1][m + 1];
        f[0][0] = true;
        for (int i = 0; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (p.charAt(j - 1) == '*') {
                    f[i][j] = f[i][j - 2];
                    if (matches(s, p, i, j - 1)) {
                        f[i][j] = f[i][j] || f[i - 1][j];
                    }
                } else {
                    // 如果 p 的第 j 个字符是一个小写字母,那么我们必须在 s 中匹配一个相同的小写字母
                    if (matches(s, p, i, j)) {
                        f[i][j] = f[i - 1][j - 1];
                    }
                }
            }
        }
        return f[n][m];
    }

    private boolean matches(String s, String p, int i, int j) {
        if (i == 0) {
            return false;
        }
        if (p.charAt(j - 1) == '.') {
            return true;
        }
        return s.charAt(i - 1) == p.charAt(j - 1);
    }
}

(全文完)