动态规划解最长公共子序列LCS (java描述)

线性DP不像背包问题有模板,少数常见的线性 DP 问题也有模板,比如LIS 和 LCS 问题

目录

问题描述:

分析:

计算LCS的长度

逆序递归构造LCS

完整代码+注释


问题描述:

给定两个序列X <x1,x2,x3...xm> 和Y <y1,y2,y3...yn>,求他们最长的公共子序列

如X="ABCBDAB" Y="BDCABA" ans="BCBA";

暴力方法的运行时间为指数阶,不实用。

使用动态规划来解:

分析:

设Z=<z1,z2,z3....zk> 是 X<x1,x2,x3...xm> Y<y1,y2,y3...yn> 的LCS

则有三种情况

第一种情况:如果xm = yn,则zk = xm = yn 且 Zk-1是Xm-1和Yn-1的一个LCS

第一种情况:如果xm != yn 且 zk != xm,则Z是Xm-1和Y的一个LCS

第一种情况:如果xm != yn 且 zk != yn,则Z是X和Yn-1的一个LCS

我们可以定义c[i,j]便是Xi和Yi的LCS长度,可以得到:

下面代码:

过程中还需要维护一个数组b帮助构造最优解

计算LCS的长度

        int[][] b = new int[m+1][n+1];
        int[][] c = new int[m+1][n+1];
        //初始化上边和左边为0
        for(int i=0;i<=m;i++) c[i][0]=0;   
        for(int i=0;i<=n;i++) c[0][i]=0;
        
        //从1开始,左边和右边初始化为0
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                //这里注意s的坐标要-1,因为ij是从1开始的,但是字符串是从0开始的
                if(s1[i-1]==s2[j-1])
                {
                    c[i][j] = c[i-1][j-1]+1;
                    b[i][j] = 1; //1表示箭头左上
                }
                //比较上和左,哪方大就等于谁
                else if(c[i-1][j] >= c[i][j-1]) //左大
                {
                    c[i][j] = c[i-1][j];
                    b[i][j] = 2; //2表示箭头向左
                }
                else //上大
                {
                    c[i][j] = c[i][j-1];
                    b[i][j] = 3; //3表示箭头向右
                }
            }
        }

图中阴影序列中每个“↖”对应的表项表示xi==yj是LCS的一个元素

c[m][n]保存了LCS的长度,时间复杂度为O(mn)

逆序递归构造LCS

    public static void printLCS(int[][] b,char[] s1,int m,int n)
    {
        if(m==0 || n==0) return;
        if(b[m][n]==1) 
        {
            printLCS(b,s1,m-1,n-1);
            System.out.print(s1[m-1]); //记得字符串-1,因为是从0开始的
        }
        else if(b[m][n]==2) printLCS(b,s1,m-1,n);
        else printLCS(b,s1,m,n-1);
    }

时间复杂度为O(m+n) ,结果打印出BCBA

完整代码+注释

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException{
        //BufferedReader scan = new BufferedReader(new InputStreamReader(System.in));
        Scanner scan = new Scanner(System.in);
        String s1 = "ABCBDAB";
        String s2 = "BDCABA";
        LCS(s1,s2);
    }
    
    public static void LCS(String S1,String S2)
    {
        char[] s1 = S1.toCharArray();
        char[] s2 = S2.toCharArray();
        
        int m = s1.length;
        int n = s2.length;
        int[][] b = new int[m+1][n+1];
        int[][] c = new int[m+1][n+1];
        //初始化上边和左边为0
        for(int i=0;i<=m;i++) c[i][0]=0;   
        for(int i=0;i<=n;i++) c[0][i]=0;
        
        
        //从1开始,左边和右边初始化为0
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                //这里注意s的坐标要-1,因为ij是从1开始的,但是字符串是从0开始的
                if(s1[i-1]==s2[j-1])
                {
                    c[i][j] = c[i-1][j-1]+1;
                    b[i][j] = 1; //1表示箭头左上
                }
                //比较上和左,哪方大就等于谁
                else if(c[i-1][j] >= c[i][j-1]) //左大
                {
                    c[i][j] = c[i-1][j];
                    b[i][j] = 2; //2表示箭头向左
                }
                else //上大
                {
                    c[i][j] = c[i][j-1];
                    b[i][j] = 3; //3表示箭头向右
                }
            }
        }
        
        //打印c数组,c[m][n]即为LCS长度
        /*for(int i=0;i<=m;i++)
        {
            for(int j=0;j<=n;j++)
            {
                System.out.print(c[i][j]+" ");
            }
            System.out.println();
        }*/
        for(int i=0;i<=m;i++)
        {
            for(int j=0;j<=n;j++)
            {
                System.out.print(b[i][j]+" ");
            }
            System.out.println();
        }
        
        //逆序递归构造LCS
        printLCS(b,s1,m,n);
        
    }
    public static void printLCS(int[][] b,char[] s1,int m,int n)
    {
        if(m==0 || n==0) return;
        if(b[m][n]==1) 
        {
            printLCS(b,s1,m-1,n-1);
            System.out.print(s1[m-1]); //记得字符串-1,因为是从0开始的
        }
        else if(b[m][n]==2) printLCS(b,s1,m-1,n);
        else printLCS(b,s1,m,n-1);
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值