動態規划算法之最長公共子序列問題


一、問題描述

求兩個字符序列的公共最長子序列。例如字符序列abcbdb和字符序列acbbabdbb的最長公共子序列為acbdb。

二、問題分析

(1)用L[i][j]表示子序列xi和yj的最長公共子序列的長度,動態規划函數為

L[i][j] = L[i - 1][j - 1] + 1,   xi等於yj

         = max(L[i][j - 1], L[i - 1][j]),     xi不等於yj

邊界條件第0行和第0列均為0,即L[i][0] = L[0][j] = 0

上例中L填寫情況如下



(2)因為不只要求出最大長度,還要尋找到公共最長子序列,所以在填表L[i][j]過程中,再填一個表S[i][j],

xi等於yj,設置S[i][j] = 1;

xi不等於yj,並且len[i + 1][j] >= len[i][j + 1],設置S[i][j] = 2;

xi不等於yj,並且len[i + 1][j] < len[i][j + 1],設置S[i][j] = 3;

填表S[i][j]完成后,具體如何找到最長公共子序列詳見代碼注釋。

上例中S填寫情況及尋找最長公共子序列過程如下


三、算法代碼

public static void maxCommonChar(char [] a, char [] b){
int m = a.length;
int n = b.length;
int [][] len = new int[m + 1][n + 1];//保存動態規划過程中的公共子串長度
int [][] flags = new int[m + 1][n + 1];//保存動態規划過程中的標志位
for(int i = 0; i <= m - 1; i++){//實現動態規划函數
for(int j = 0; j <= n - 1; j++){
if(a[i] == b[j]){//規划函數len[i + 1][j + 1] = len[i][j] + 1, a[i] == b[j]
len[i + 1][j + 1] = len[i][j] + 1;
flags[i + 1][j + 1] = 1; //設置標志位
}else if(len[i + 1][j] >= len[i][j + 1]){
len[i + 1][j + 1] = len[i + 1][j];
flags[i + 1][j + 1] = 2;
}else{
len[i + 1][j + 1] = len[i][j + 1];
flags[i + 1][j + 1] = 3;
}
}
}
int k = len[m][n]; //最長公共子串長度
char [] commonChars = new char[k];//保存最長公共子串
int i = m, j = n;
for(;i > 0 && j > 0;){
if(flags[i][j] == 1){//只有標志位為1相應位置上的字符才為公共字符
commonChars[k - 1] = a[i - 1];
k--;
i--;
j--;
}else if(flags[i][j] == 2){
j--;
}else{
i--;
}
}
System.out.println("最長公共子序列長度為:" + len[m][n]);
System.out.print("最長公共子序列為:");
for(int l = 0; l <= len[m][n] - 1; l++){
System.out.print(commonChars[l] + " ");
}
}
四、完整測試代碼

public class package01 {

public static void main(String [] args){
char [] a = new char[]{'a', 'b', 'c', 'b', 'd', 'b'};
char [] b = new char[]{'a', 'c', 'b', 'b', 'a', 'b', 'd', 'b', 'b'};
maxCommonChar(a, b);
}

public static void maxCommonChar(char [] a, char [] b){
int m = a.length;
int n = b.length;
int [][] len = new int[m + 1][n + 1];//保存動態規划過程中的公共子串長度
int [][] flags = new int[m + 1][n + 1];//保存動態規划過程中的標志位
for(int i = 0; i <= m - 1; i++){//實現動態規划函數
for(int j = 0; j <= n - 1; j++){
if(a[i] == b[j]){//規划函數len[i + 1][j + 1] = len[i][j] + 1, a[i] == b[j]
len[i + 1][j + 1] = len[i][j] + 1;
flags[i + 1][j + 1] = 1; //設置標志位
}else if(len[i + 1][j] >= len[i][j + 1]){
len[i + 1][j + 1] = len[i + 1][j];
flags[i + 1][j + 1] = 2;
}else{
len[i + 1][j + 1] = len[i][j + 1];
flags[i + 1][j + 1] = 3;
}
}
}
int k = len[m][n]; //最長公共子串長度
char [] commonChars = new char[k];//保存最長公共子串
int i = m, j = n;
for(;i > 0 && j > 0;){
if(flags[i][j] == 1){//只有標志位為1相應位置上的字符才為公共字符
commonChars[k - 1] = a[i - 1];
k--;
i--;
j--;
}else if(flags[i][j] == 2){
j--;
}else{
i--;
}
}
System.out.println("最長公共子序列長度為:" + len[m][n]);
System.out.print("最長公共子序列為:");
for(int l = 0; l <= len[m][n] - 1; l++){
System.out.print(commonChars[l] + " ");
}
}
}
五、運行結果

最長公共子序列長度為:5
最長公共子序列為:a c b d b





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com