一天一道算法题(4)---最长公共子序列

  • 题目

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

  • 解析

本题是非常经典的动态规划问题,先来介绍求解动态规划表的过程。如果str1的长度为M,str2的长度为N,生成大小为M*N的矩阵dp,行数为M,列数为N。dp[i][j]的含义是str1[0..i]和str2[0..j]的最长公共子序列的长度。从左到右,再从上到下计算矩阵dp。

1.矩阵dp第一列即dp[0..M-1][0],dp[i][0]的含义是str1[0..i]与str2[0]的最长公共子序列的长度。

2.矩阵dp第一行即dp[0][0..N-1]与步骤一同理,如果str1[0]==str2[j],则令dp[0][j]=1

3.对于其它位置(i,j),dp[i][j]的值只可能来自以下三种情况:

  • 可能是dp[i-1][j]
  • 可能是dp[i][j-1]
  • 可能是dp[i-1][j-1],就是str1[i]==str2[j]的时候,dp[i][j]=dp[i-1][j-1]+1

这三个可能的值中,选最大的作为dp[i][j]的值,具体参看下面代码getdp方法。

  public int[][] getdp(char[] str1, char[] str2) {
        int[][] dp = new int[str1.length][str2.length];
        dp[0][0] = str1[0] == str2[0] ? 1 : 0;
        for (int i = 1; i < str1.length; i++) {
            dp[i][0] = Math.max(dp[i-1][0], str1[i] == str2[0] ? 1 : 0);  //最大也就是1了,因为只比较了str2的一个字符
        }
        for (int j = 1; j < str2.length; j++) {
            dp[0][j] = Math.max(dp[0][j-1], str1[0] == str2[j] ? 1 : 0); //同上
        }
        for (int i = 1; i < str1.length; i++) {
            for (int j = 1; j < str2.length; j++) {
                dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                if (str1[i] == str2[j]) {
                    dp[i][j] = Math.max(dp[i][j], dp[i-1][j-1] + 1); //找到3中方式中最大的长度
                }
            }
        }
        return dp;
    }

dp矩阵中最右下角的值代表str1和str2的最长公共子序列的长度。通过整个dp矩阵可以得到最长公共子序列。具体如下:

1.从矩阵的右下角开始,有三种移动方式:向上,向左,向左上。假设移动过程中,i表示此时的行数,j表示此时的列数,同时用一个变量res来表示最长公共子序列。

2.如果dp[i][j]大于dp[i-1][j]和dp[i][j-1],说明在计算dp[i][j]的时候,一定是选择dp[i-1][j-1],可以确定str1[i]等于str2[j],并且这个字符一定属于最长公共子序列,把这个字符放进res,然后向左上方移动。

3.如果dp[i][j]等于dp[i-1][j],向上方移动

4.如果dp[i][j]等于dp[i][j-1],同上,向左方移动。

5.如果dp[i][j]同时等于dp[i-1][j]和dp[i][j-1],向上还是向下无所谓

通过dp求解最长公共子序列的过程:

  public String lcse(String str1, String str2) {
        if (str1 == null || str2 == null || str1.equals("") || str2.equals("")) {
            return "";
        }
        char[] chs1 = str1.toCharArray();
        char[] chs2 = str2.toCharArray();
        int[][] dp = getdp(chs1, chs2);
        int m = chs1.length - 1;
        int n = chs2.length - 1;
        char[] res = new char[dp[m][n]];
        int index = res.length - 1;
        while (index >= 0) {
            if (n > 0 && dp[m][n] == dp[m][n-1]) {
                n--;
            } else if (m > 0 && dp[m][n] == dp[m-1][n]) {
                m--;
            } else {
                res[index--] = chs1[m];
                m--;
                n--;
            }
        }
        return String.valueOf(res);
    }

这只是打印出一种子序列,如果把所有的子序列都打印出来怎么做,明天更新。

  • 参考资料

《程序员代码面试指南》  左程云

时间: 2025-01-06 04:45:47

一天一道算法题(4)---最长公共子序列的相关文章

算法——动态规划篇——最长公共子序列

问题描述      最长公共子序列,英文缩写为LCS(Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列.       解决最长公共子序列,一种常用的办法,就是穷举法,组合出所有的情况,但是这样对于长序列的情况来说,是非常不实际.. 假设现在有两个序列,x[]={'A','B','C','B','D','A','B'};y[]={'B','D','C','A'

动态规划算法之:最长公共子序列 & 最长公共子串(LCS)

1.先科普下最长公共子序列 & 最长公共子串的区别: 找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的.而最长公共子序列则并不要求连续. 2.最长公共子串 其实这是一个序贯决策问题,可以用动态规划来求解.我们采用一个二维矩阵来记录中间的结果.这个二维矩阵怎么构造呢?直接举个例子吧:"bab"和"caba"(当然我们现在一眼就可以看出来最长公共子串是"ba"或"ab") b a b c 0 0 0 a 0 1

【算法】 求最长公共子序列

最长公共子序列 算法这玩意儿我完全是外行,因为从头开始就不是这个专业的再加上从小就对逻辑性强的东西苦手..所以一直没什么机会也没什么兴趣学.去年刚开始学习了python的那段时间曾经碰到过几个算法比较高级的问题,当时觉得果然这不是我的能力能驾驭的了的..总之是先记录了下来,但是对于算法这块将来的拓展和进一步学习,其实我挺没信心的 = = 问题:最长公共子序列问题(Longest Common Sequence) 子序列是指一个字符串抽掉0到若干个字符后剩下的字符串,抽取的字符不一定相邻也不一定有

算法设计 - LCS 最长公共子序列&amp;&amp;最长公共子串 &amp;&amp;LIS 最长递增子序列

出处 http://segmentfault.com/blog/exploring/ 本章讲解:1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度:2. 与之类似但不同的最长公共子串方法.最长公共子串用动态规划可实现O(n^2)的时间复杂度,O(n^2)的空间复杂度:还可以进一步优化,用后缀数组的方法优化成线性时间O(nlogn):空间也可以用其他方法优化成线性.3.LIS(最长递增序列)DP方法可实现O(n^2)的时间复杂度,进一步优化最佳可达到O(nlogn)

[算法]动态规划之最长公共子序列

最长公共子序列 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<time.h> 4 #include<string.h> 5 6 #define N 5 7 8 int max(int a, int b, int c) { 9 10 int ab = a>b ? a : b; 11 return ab > c ? ab : c; 12 13 } 14 15 void solve(int *a

动态规划之最长公共子序列(LCS)

tips : 本文内容是参考了很多著名博客和自己的思考得出的,如有不当欢迎拍砖. 先简单说一下动态规划 通俗地说:动态规划就是将一个可以划分为子问题的问题进行递归求解,不过动态规划将大量的中间结果保存起来, 不管它们是否会用得到,从而在后面的递归求解过程中可以快速求解.由此可以看得出来动态规划是一个以牺牲空间 为代价换取时间的算法. 对于最长公共子序列的题目不用多说,现在来分析一下LCS的动态规划解决思路: 一.首先先观察问题是否符合动态规划最明显的两个特征:最优子结构和重叠子问题 方便起见,以

ACM试题 - 最长公共子序列 - 动态规划方法

ACM试题题源-(最长公共子序列):http://acm.nyist.net/JudgeOnline/problem.php?pid=36 提交代码: import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner cin = new Scanner(System.in); int n = cin.nextInt(); int[] len = new int[n]; fo

一天一道算法题(5)---最长公共子串

题目 给定两个字符串str1和str2,返回两个字符串的最长公共子串.例如:str1="1AB2345CD",str2="12345EF",公共子串是"2345" 解析 最长公共子串和最长公共子序列的区别是,子串是连续的,子序列是不连续的. 首先还是要生成动态规划表.生成大小为M*N的矩阵dp.dp[i][j]的含义是,在必须把str1[i]和str2[j]当作公共子串最后一个字符的情况下,公共子串最长能有多长.比如,str1="A12

一天一道算法题---6.3---二分运用

感谢微信平台 : 一天一道算法题 -----  一天多一点进步----- 先来段 废话: 已经 3 4天没更了 主要还是自己 太懒了 .... 好 让我们 开始吧 题目链接: touch  me 是不是 有点长啊  最可恶的是 还有张 诱人的 馅饼  --- 话说 有个很好的美国系列校园青春 sex movie ---  American Pie  --- 美国派----- 题目大意: 有F+1个人来分N个圆形蛋糕 每个人得到的必是一整块蛋糕 (不是由几块拼在一起的)面积要相同 求每个人最多能得