LCS

给定俩个序列X,Y,求出俩序列中最长公共子序列,子序列可以是不连续的。拿到这样的问题,如果使用暴力法,是枚举出X所有的子序列,判断子序列是否为Y的子序列,然后再从所有的子序列中找出长度最长的。这样做的话,时间复杂度为O(2^N),时间复杂度太高。在这种通过暴力法解决时,时间复杂度过高的情况下,我们可以考虑动态规划法。

序列:
X={x1,x2,,,xn}
Y={y1,y2,,,ym}

1.确定子问题
采用动态规划第一步确定子问题,求Xn和Ym的最长公共子序列
如果xn = ym,Xn与Ym的最长公共子序列问题转换为求Xn-1与Ym-1的最长公共子序列
如果xn != ym,Xn与Ym的公共子序列问题转换为求Xn与Ym-1,Xn-1与Ym中的最长公共子序列问题

2.状态转移方程
dp[i][j] = dp[i-1][j-1]+1 如果x[i] = y[j]
dp[i][j] = max{dp[i][j-1],dp[i-1][j]} 如果x[i] != y[j]
其中dp数组中存放的是长度

3.填充dp数组
LCS_第1张图片

上述dp数组记录了最长公共子序列长度的演变过程,最长公共子序列长度为DP[lengthX][lengthY],如何获取最长公共子序列,从DP[lengthX][lengthY]出发,还原其生成过程,当然这里存在多种生成方式,我们取其中一种。

4.代码
这里采用了一个辅助数组temp[][]用于构造LCS。


/***
 * 最长公共子序列
 */
public class LongestCommanSeq {
    public static void main(String[] args){
        char[] a = {'A','B','C','B','D','A','B'};
        char[] b = {'B','D','C','A','B','A'};
        getLCS(a,b);
    }

    /**
     * 最长公共子序列
     * @param a
     * @param b
     */
    private static void getLCS(char[] a, char[] b) {
        if (a == null || b == null || a.length == 0 || b.length == 0){
            System.out.println("no LCS");
            return;
        }
        int lengthA = a.length;
        int lengthB = b.length;
        int[][] dp = new int[lengthA+1][lengthB+1];
        char[][] temp = new char[lengthA+1][lengthB+1];
        for (int i = 1; i < lengthA+1; i++){
            for (int j = 1; j < lengthB+1; j++){
                if (a[i-1] == b[j-1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    temp[i][j] = 'x';
                }
                else {
                    if (dp[i][j-1] >= dp[i-1][j]){
                        dp[i][j] = dp[i][j-1];
                        temp[i][j] = 'z';
                    }else {
                        dp[i][j] = dp[i-1][j];
                        temp[i][j] = 's';
                    }
                }
            }
        }
        System.out.println(dp[lengthA][lengthB]);
        printLCS(temp,a,lengthA,lengthB);
        printLCSS(dp,a,lengthA,lengthB);
    }

    private static void printLCS(char[][] temp,char[] a,int i,int j) {
        if (i == 0 || j == 0)
            return;
        if (temp[i][j] == 'x') {
            printLCS(temp,a, i - 1,j - 1);
            System.out.print(a[i-1]+" ");
        }else if(temp[i][j] == 's')
            printLCS(temp,a,i-1,j);
        else
            printLCS(temp,a,i,j-1);
    }
}

优化:由于LCS构造只与dp[i-1][j-1],dp[i-1][j],dp[i][j-1]有关,所以可以优化空间,改造打印LCS方法

 private static void printLCSS(int[][] dp, char[] a, int i, int j) {
        if (i == 0 || j == 0)
            return;
        if (dp[i][j] == dp[i-1][j-1]+1){
            printLCSS(dp,a,i-1,j-1);
            System.out.print(a[i-1]+" ");
        }else if (dp[i][j] == dp[i-1][j]){
            printLCSS(dp,a,i-1,j);
        }else {
            printLCSS(dp,a,i,j-1);
        }
    }

你可能感兴趣的:(算法)