圖文詳解動態規劃原理——鏈接
相關概念
子序列形式化定義:
給定一個序列X=<x1,x2,x3,x4...,xm>,另一個序列Z=<z1,z2,z3,z4...,zk>,若存在一個嚴格遞增的X的下標序列<i1,i2,i3,...,ik>對所有的1,2,3,...,k,都滿足x(ik)=zk,則稱Z是X的子序列比如Z=<B,C,D,B>是X=<A,B,C,B,D,A,B>的子序列
公共子序列定義:
如果Z既是X的子序列,又是Y的子序列,則稱Z爲X和Y的公共子序列最長公共子序列(以下簡稱LCS):
2個序列的子序列中長度最長的那個
動態規劃求解最長公共子序列:
分析規律:
設X=<x1,x2,x3,x4...,xm>,Y=<y1,y2,y3,y4...,yn>爲兩個序列,Z=<z1,z2,z3,z4...,zk>是他們的任意公共子序列經過分析,我們可以知道:
1、如果xm = yn,則zk = xm = yn 且 Zk-1是Xm-1和Yn-1的一個LCS
2、如果xm != yn 且 zk != xm,則Z是Xm-1和Y的一個LCS
3、如果xm != yn 且 zk != yn,則Z是X和Yn-1的一個LCS
所以如果用一個二維數組c表示字符串X和Y中對應的前i,前j個字符的LCS的長度話,可以得到以下公式:
dp[i][j]的意思表示的是numA以i結尾,numB以j結尾的最長子序列的長度,那麼dp[0][i]j就是表示numA[0]和numB以i結尾的最長子序列的長度,所以如果相等時就是1,不等時是0
最長公共子序列的 長度 + 輸出所有的最長公共子序列:
import java.util.Scanner;
import java.util.TreeSet;
public class Main04_1 {
public static void main(String [] args){
Scanner sc = new Scanner(System.in);
String str1 = sc.nextLine();
String str2 = sc.nextLine();
char [] numsA = str1.toCharArray();
char [] numsB = str2.toCharArray();
int [][] dp = getMaxLCSLength2(numsA, numsB);
int len = dp[numsA.length][numsB.length];
String lcs_str = "";
traceBack(dp, numsA, numsB, numsA.length,numsB.length,lcs_str);
System.out.println(len);
}
//求最長公共子序列的長度,
public static int[][] getMaxLCSLength2(char [] arr1, char [] arr2){
int len1 = arr1.length;
int len2 = arr2.length;
int [][] dp = new int[len1+1][len2+1];//此處的動態規劃數組長度要比原長度多加1,需要多存儲一行0和一列0
for(int i = 0;i<=len2;i++){////第0行第i列全部賦值爲0 //這裏是<=號
dp[0][i] = 0;
}
for(int i = 0;i<=len1;i++){//第i行,第0列全部爲0 //這裏是<=號
dp[i][0] = 0;
}
for(int i = 1;i<=len1;i++){//這裏是<=號
for(int j = 1;j<=len2;j++){//這裏是<=號
if(arr1[i-1] == arr2[j-1]){//注意這裏若是arr1[i] == arr2[j],就會發生數組越界
dp[i][j] = dp[i-1][j-1] + 1;
}else {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
//return dp[len1][len2];
return dp;
}
//功能:回溯,求出所有的最長公共子序列,並放入set中
public static void traceBack(int [][] dp, char [] arr1, char [] arr2, int i, int j, String lcs_str) {
TreeSet<String> set = new TreeSet<String>();
while (i>0 && j>0) {
if (arr1[i-1] == arr2[j-1]) {
lcs_str += arr1[i-1];
i--;
j--;
}
else {
if (dp[i][j-1]>dp[i-1][j])
j--;
else if (dp[i][j-1]<dp[i-1][j])
i--;
else { // 相等的情況
traceBack(dp, arr1, arr2, i-1, j, lcs_str);
traceBack(dp, arr1, arr2, i, j-1, lcs_str);
return;
}
}
}
set.add(reverse(lcs_str));
//輸出最長公共子序列
for(String s : set) {
System.out.println(s);
}
}
//功能:字符串逆序
public static String reverse(String str) {
StringBuffer strBuf = new StringBuffer(str).reverse();
return strBuf.toString();
}
}
運行結果
abcbdab
bdcaba
bcba
bcab
bdab
4