最长公共子串和最长公共子序列

360 阅读1分钟

两个经典的动态规划问题。

最长公共子串问题

【题目】 求两个字符串str1和str2的最长公共子串的长度。

方法一 暴力法

思路:取短的字符串,求出其所有子串,然后在长的字符串中进行查找(String#contains方法)。

【代码】

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            String s1 = sc.nextLine();
            String s2 = sc.nextLine();
            System.out.println(f(s1, s2));
        }
    }
    
    private static int f(String s1, String s2) {
        if (s1.length() < s2.length()) {
            return f(s2, s1);
        }
        Set<String> set = new HashSet<>();
        for (int i = 0; i < s2.length(); i++) {
            for (int j = i + 1; j < s2.length() + 1; j++) {
                set.add(s2.substring(i, j));
            }
        }
        int max = 0;
        for (String s : set) {
            if (s.length() <= max) {
                continue;
            }
            if (s1.contains(s)) {
                max = Math.max(max, s.length());
            }
        }
        return max;
    }
}

方法二 动态规划

dp[i][j]的含义是:在必须把str1[i]str2[j]当作公共子串的最后一个字符的情况下,公共子串的最大值。 那么:如果str1[i] == str2[j]dp[i][j] = dp[i - 1][j - 1] + 1;如果str1[i] != str2[j]dp[i][j] = 0

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            String s1 = sc.nextLine();
            String s2 = sc.nextLine();
            System.out.println(f(s1, s2));
        }
    }
    
    private static int f(String s1, String s2) {
        int[][] dp = new int[s1.length()][s2.length()];
        // 0 column
        for (int i = 0; i < s1.length(); i++) {
            dp[i][0] = s1.charAt(i) == s2.charAt(0) ? 1 : 0;
        }
        // 0 row
        for (int i = 0; i < s2.length(); i++) {
            dp[0][i] = s2.charAt(i) == s1.charAt(0) ? 1 : 0;
        }
        int max = 0;
        for (int i = 1; i < s1.length(); i++) {
            for (int j = 1; j < s2.length(); j++) {
                if (s1.charAt(i) == s2.charAt(j)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    max = Math.max(max, dp[i][j]);
                }
            }
        }
        return max;
    }
}

最长公共子序列问题

dp[i][j]的含义是:str[0..i]str[0..j]的最长公共子序列的长度。 与最长公共子串有一点差别:

dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
if (str1[i] == str2[j]) {
	dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1);
}