1. 程式人生 > >動態規劃:最長公共子串 & 最長公共子序列

動態規劃:最長公共子串 & 最長公共子序列

一、最長公共子串

1. 題目

給定兩個序列 X 和 Y,如果 Z 即是 X 的子串,又是 Y 的子串,我們就稱它是 X 和 Y 的公共子串,注意子串是連續的。 例如 X = { A, B, C, D, E, F, G },Y = { A, B, Z, D, E, F, K, G },那麼它們最長的公共子串即 { D, E, F }

2. 思路

藉助一下《圖解演算法》中的例子。假設對於兩個字串 fishhish,我們可以繪製一個這樣的表格,來求解它們最長的公共子串,即: 圖1 注意,最長公共子串的最終答案並不一定在最後一個格子裡,所以我們還需要一個變數 max 來記錄最大值。

3. 程式碼

public
class Main { public static void main(String[] args) { String s1 = "ABCDEFG"; String s2 = "ABZDEFKG"; System.out.println("最長公共子串長度:" + getLCS(s1, s2)); } public static int getLCS(String s1, String s2) { char[] a = s1.toCharArray(); char[] b = s2.
toCharArray(); // a.length行,b.length列 int[][] result = new int[a.length + 1][b.length + 1]; int max = 0; for (int i = 0; i < a.length; i++) { for (int j = 0; j < b.length; j++) { if (a[i] == b[j]) { result[i + 1][j + 1]
= result[i][j] + 1; max = Math.max(max, result[i + 1][j + 1]); } } } // ----- print table ----- System.out.print(" "); for (int i = 0; i < b.length; i++) { System.out.print(" " + b[i]); // 列印第一行 } System.out.println(); for (int i = 1; i < result.length; i++) { System.out.print(a[i - 1] + " "); for (int j = 1; j < result[i].length; j++) { System.out.print(result[i][j] + " "); } System.out.println(); } System.out.println(); // ----------------------- return max; } }

注意上面程式碼中 print table 列印部分僅是為了給大家展示表格幫助理解,實際中並不需要。 執行結果:

  A B Z D E F K G
A 1 0 0 0 0 0 0 0 
B 0 2 0 0 0 0 0 0 
C 0 0 0 0 0 0 0 0 
D 0 0 0 1 0 0 0 0 
E 0 0 0 0 2 0 0 0 
F 0 0 0 0 0 3 0 0 
G 0 0 0 0 0 0 0 1 
 
最長公共子串長度:3

如果我們還需要知道具體的最長公共子串是什麼,那麼就需要再新增一個變數,記錄最大值出現的位置,然後往前取最長公共子串的長度即可。

一、最長公共子序列

1. 題目

和最長公共子串類似,只不過子序列是可以不連續的。 例如 X = { A, B, C, D, E, F, G },Y = { A, B, Z, D, E, F, K, G },那麼它們最長的公共子串即 { A, B, D, E, F, G }

2. 思路

還是借鑑一下《圖解演算法》裡的例子吧,表格繪製過程如下: 在這裡插入圖片描述 注意最長公共子序列的話,最大值一定會出現在最後一個格子裡。

3. 程式碼

public class Main {
 
    public static void main(String[] args) {
        String s1 = "ABCDEFG";
        String s2 = "ABZDEFKG";
        System.out.println("最長公共子序列長度:" + getLCS(s1, s2));
    }
 
    public static int getLCS(String s1, String s2) {
        char[] a = s1.toCharArray();
        char[] b = s2.toCharArray();
        // a.length行,b.length列
        int[][] result = new int[a.length + 1][b.length + 1];
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < b.length; j++) {
                if (a[i] == b[j]) {
                    result[i + 1][j + 1] = result[i][j] + 1;
                } else {
                    result[i + 1][j + 1] = Math.max(result[i][j + 1], result[i + 1][j]);
                }
            }
        }
        // ----- print table -----
        System.out.print(" ");
        for (int i = 0; i < b.length; i++) {
            System.out.print(" " + b[i]); // 列印第一行
        }
        System.out.println();
        for (int i = 1; i < result.length; i++) {
            System.out.print(a[i - 1] + " ");
            for (int j = 1; j < result[i].length; j++) {
                System.out.print(result[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println();
        // -----------------------
        return result[a.length][b.length];
    }
 
}

注意上面程式碼中 print table 列印部分僅是為了給大家展示表格幫助理解,實際中並不需要。 執行結果:

  A B Z D E F K G
A 1 1 1 1 1 1 1 1 
B 1 2 2 2 2 2 2 2 
C 1 2 2 2 2 2 2 2 
D 1 2 2 3 3 3 3 3 
E 1 2 2 3 4 4 4 4 
F 1 2 2 3 4 5 5 5 
G 1 2 2 3 4 5 5 6 
 
最長公共子序列長度:6