输出最长公共子序列的长度+打印所有的最长公共子序列+动态规划+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

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值