java实现最长连续子序列_漫画:最长公共子序列

de5628037e5dd5bdd39b207d3e41cfe4.png

6e5d76661abd73c2ac3ddace000627ba.png

4156aaec9e6e8620c909ebb913e67e5e.png

题目:给定两个字符串 str1 和 str2,返回这两个字符串的最长公共子序列的长度

解释:一个字符串的子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串,如下图示:

a70350c5dd818241e3ad29e8bca2f853.png

也就是说对于以下两个字符串 str1 和 str2,其最长公共子串为 「acg」。

b1fb8006f1292f25deb2dddbc4e85d28.png

08786ecc375d5396962c55c28b7ccf99.png

4a59bc2ee24f380bca1b644f8941b088.png

1e911b7e6f17f8f00284dd15378cb63d.png

阿宝的想法dp 是个二维数组,即 dp[i][j], 表示对于子串 str1[0..i] 与子串 str2[0..j], 它们的最长公共子序列长度为 dp[i][j],这样的话根据定义, dp[str1.length-1][str2.length-1] 即为所求的解。

f1d2a7b7ef0b938b21b3f2aa5a66488e.png

3759732ae1db2102ae754bb87305cd13.png

阿宝画的状态转移表:

5f9c3d2970420bddcecc3d3e6b6e704d.png

04963f6c221efc4b68331d8ddeb299e4.png

edeaee1c93dbbf60af1e3dabdbd059dc.png

93f4bbc2a701dad2c4cdae73b937c5be.png
  1. 当两个字符串 i,j 索引对应的字符相等时,如下图示

31d9cf3d61b5f7e11d32c31f5b993f00.png

显然dp[i][j] = dp[i-1][j-1] +1, 1 代表 i 和 j 指向的字符相等,dp[i-1][j-1] 代表除此相同字符外的 i,j 索引之前字符串的公共子序列。 2. 当两个字符串 i,j 索引对应的字符不相等时

50bc88306c7bcca782f9a61f723b890d.png

此时 dp[i][j] 值可能为 dp[i-1][j] 或 dp[i][j-1], dp[i-1][j] 怎么理解,既然 i 与 j 指向的字符不等,那只要丢弃 i 字符,求 str1[0..i-1] 与 str2[0..j] 的最长公共子序列即可,即 dp[i-1][j], 同理对于dp[i][j-1],即为丢弃 j ,求 str1[0..i] 与 str2[0..j-1] 的最长公共子序列

dfe1679038eb406e2e8786c979ab6a4a.png

既然 dp[i][j] 有可能等于这两个值,那么显然应该取这两者的较大值,

即 dp[i][j] = max(dp[i-1][j], dp[i][j-1])。综上可知状态状态方程如下:

f9c6456853b498459bd3e25a5204a2c4.png

8d5daf813be34fcdcd184bd530c235aa.png

8f9ce13a5595f69369c77323f3121a4b.png

阿宝的想法:

空字符串与任何字符串的最长公共子序列都为 0,所以 dp[0][i], dp[j][0] 都为 0(i

为 0 到 str1 的长度, j 为 0 到 str2 的长度),如下图蓝色部分即为 base case。

cfa6b35a2e2a7af58fcca3ad62c894be.png

59310b6ffb84e5d05075dc6e55cd4ea0.png

代码如下

public class Solution {
    public static int getLCS(char[] x, char[] y) {
        // base case,以下 dp 中的每个元素默认值都为 0。
        int dp[][] = new int[x.length][y.length];
        for (int i = 1; i < x.length; i++) {
            for (int j = 1; j < y.length; j++) {
                // 以下逻辑为状态转移方程
                if (x[i] == y[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[x.length-1][y.length-1];
    }

    public static void main(String[] args) {
        char[] x =  {' ', 'a', 'b', 'c', 'e', 'f', 'g'};
        char[] y =  {' ', 'a', 'c', 'd', 'g'};
        int lcs = getLCS(x, y);
        System.out.printf("lcs = " + lcs);
    }
}

d4ae79ae5046312432cd82c93d95af41.png

f7c359478928fee5f76192b24fdd0866.png

271f6e0ce0b78a9896dd9ac839a8ffa2.png

6c3149520aac392f4721ea4914a853e1.png

总结对于动态规划题型,其实套路大体相似,无非就是求出 dp 方程,再自下而上的求解,对于字符串类的动态规划题型,定义好可以先画出状态转移表,然后再据此找出状态转移方程,状态转移方程的推导有一定的技巧,根据状态(比如文中 i 和 j 对应字符是否相等)可能会有不同的情况,可以多考虑下对应的字符选或不选对应的 dp 是啥,据此推导 dp 会容易一些

来源:码海

作者:码海

原文:漫画:最长公共子序列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值