最長公共子序列問題;打印最長公共子序列及打印所有解問題


package test;

import java.util.Scanner;
import java.util.TreeSet;

public class algo {

// 這里傳參數字符數組,還可以是字符串
public static int[][] getMaxLCSLength2(char [] arr1, char [] arr2){
int len1 = arr1.length;
int len2 = arr2.length;
// 下面完全是根據LCS定義而走;首先處理邊界的問題,需要存儲0行0列;所以這里動態規划數組長度比原長度多1
int [][] dp = new int[len1+1][len2+1];
for(int i = 0;i<=len2;i++){ // 處理邊界,注意是<=號
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;
}

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();
}


// 這個函數只是輸出最長子序列;顯然如果只是記錄長度,則直接判斷else(xi != yj) dp[i][j] = Math.max(dp[i][j - 1], dp[i -1][j])
public static int maxlongest(String x, String y) {
int xlen = x.length();
int ylen = y.length();
int [][]dp = new int[xlen + 1][ylen + 1];
int [][]dir = new int[xlen + 1][ylen + 1];
for(int i = 1; i <= xlen; i ++) {
char xi = x.charAt(i - 1);
for(int j = 1; j <= ylen; j ++) {
char xj = y.charAt(j - 1);
if(xi == xj) {
dp[i][j] = dp[i - 1][j - 1] + 1;
dir[i][j] = 2; // 2代表是左上
}
else if(dp[i - 1][j] > dp[i][j - 1])
{
dp[i][j] = dp[i - 1][j];
dir[i][j] = 1; //1代表是向上
}
else {
dp[i][j] = dp[i][j - 1];
dir[i][j] = 3; //3代表是向左
}
}
}


int i = xlen;
int j = ylen;
StringBuffer sb = new StringBuffer();
while (i > 0 && j > 0) {
if (x.charAt(i - 1) == y.charAt(j - 1)) {
sb.append(x.charAt(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 if (dp[i][j - 1] == dp[i - 1][j]) {
// 如果是要打印所有的最長公共子序列,在這里進行保存,詳細的見最上面
i--; //此結果對於結果1所選取方向,str1的下標左移一位.替換為j--,則結果對應與結果2選取的方向
}
}
}
//由於是從后往前加入字符的,需要反轉才能得到正確結果
System.out.println(sb.reverse().toString());
return dp[xlen][ylen];
}

public static void main(String[] args) {
// 測試一:只打印一個最長公共子序列
String x = "abcbdab";
String y = "bdcaba";
int len = maxlongest(x, y);
System.out.print(len);

// 測試二:打印所有最長公共子序列
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 len1 = dp[numsA.length][numsB.length];
String lcs_str = "";
traceBack(dp, numsA, numsB, numsA.length,numsB.length,lcs_str);
System.out.println(len1);


}

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM