動態規劃:最長公共子串 & 最長公共子序列
阿新 • • 發佈:2018-12-16
一、最長公共子串
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. 思路
藉助一下《圖解演算法》中的例子。假設對於兩個字串 fish
和 hish
,我們可以繪製一個這樣的表格,來求解它們最長的公共子串,即:
注意,最長公共子串的最終答案並不一定在最後一個格子裡,所以我們還需要一個變數 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