輸出最長公共子序列的長度+打印所有的最長公共子序列+動態規劃+java

圖文詳解動態規劃原理——鏈接

相關概念
子序列形式化定義:
給定一個序列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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章