最长公共子序列、最长连续公共子序列、最长递增子序列
面试中除了排序问题,还会经常出现字符串的子序列问题,这里讲解使用动态规划解决三个常见的子序列问题:
1、最长公共子序列问题(LCS,longest-common-subsequence problem)
2、最长连续公共子序列问题
3、最长递增子序列(LIS,longest-increment-subsequence)
最长公共子序列问题LCS
问题描述:
给定两个序列X=< x1,x2,...,xm x 1 , x 2 , . . . , x m x_{1},x_{2},...,x_{m}>和Y=< y1,y2,...,yn y 1 , y 2 , . . . , y n y_{1},y_{2},...,y_{n}>,求X和Y的最长公共子序列。(子序列可以是不连续的,比如{B,C,D,B}就是{A,B,C,B,D,A,B}的子序列)
例如:输入两个字符串 BDCABA 和 ABCBDAB,字符串 BCBA 和 BDAB 都是是它们的最长公共子序列,则输出它们的长度 4,并打印任意一个子序列。
动态规划
时间复杂度O(M*N),空间复杂度O(M*N)。
在使用动态规划之前先规定一下符号,给定一个序列X=< x1,x2,...,xm x 1 , x 2 , . . . , x m x_{1},x_{2},...,x_{m}>,定义 Xi X i X_{i}为X的前缀,即 Xi X i X_{i}=< x1,x2,...,xi x 1 , x 2 , . . . , x i x_{1},x_{2},...,x_{i}>。
在求X=< x1,x2,...,xm x 1 , x 2 , . . . , x m x_{1},x_{2},...,x_{m}>和Y=< y1,y2,...,yn y 1 , y 2 , . . . , y n y_{1},y_{2},...,y_{n}>的一个LCS时,我们需要求解一个或者两个子问题。如果 xm=yn x m = y n x_{m}=y_{n},那么我们应该求解 xm−1=yn−1 x m − 1 = y n − 1 x_{m-1}=y_{n-1}的一个LCS。如果 xm≠yn x m ≠ y n x_{m}\neq y_{n},我们必须求解两个子问题:求 Xm−1 X m − 1 X_{m-1}和 Y Y Y的一个LCS与X" role="presentation" style="position: relative;">XXX和 Yn−1 Y n − 1 Y_{n-1}的一个LCS。两个LCS中较长的一个,即为X和Y的一个LCS,经过推理可知,这些子情况覆盖了所有可能出现的情况。
所以递推公式为:
具体到实际的例子中:
代码
public class LCS {public static int[][] lengthofLCS(char[] X, char[] Y){/* 构造二维数组c[][]记录X[i]和Y[j]的LCS长度 (i,j)是前缀* c[i][j]=0; 当 i = j = 0;* c[i][j]=c[i-1][j-1]+1; 当 i = j > 0; Xi == Y[i]* c[i][j]=max(c[i-1][j],c[i][j+1]); 当 i = j > 0; Xi != Y[i]* 需要计算 m*n 个子问题的长度 即 任意c[i][j]的长度* -- 填表过程*/int[][]c = new int[X.length+1][Y.length+1];// 动态规划计算所有子问题for(int i=1;i<=X.length;i++){for (int j=1;j<=Y.length;j++){if(X[i-1]==Y[j-1]){c[i][j] = c[i-1][j-1]+1;}else if(c[i-1][j] >= c[i][j-1]){c[i][j] = c[i-1][j];}else{c[i][j] = c[i][j-1];}}}// 打印C数组for(int i=0;i<=X.length;i++){for (int j=0;j<=Y.length;j++){System.out.print(c[i][j]+" ");}System.out.println();}return c;}// 输出LCS序列public static void print(int[][] arr, char[] X, char[] Y, int i, int j) {if(i == 0 || j == 0)return;if(X[i-1] == Y[j-1]) {System.out.print("element " + X[i-1] + " ");print(arr, X, Y, i-1, j-1);}else if(arr[i-1][j] >= arr[i][j-1]) {print(arr, X, Y, i-1, j);}else{print(arr, X, Y, i, j-1);}}public static void main(String[] args) {// TODO Auto-generated method stubchar[] x ={'A','B','C','B','D','A','B'}; char[] y ={'B','D','C','A','B','A'}; int[][] c = lengthofLCS(x,y);print(c, x, y, x.length, y.length);}
}
最长公共连续子序列(最长公共子串)
问题描述
给定两个序列X=< x1,x2,...,xm x 1 , x 2 , . . . , x m x_{1},x_{2},...,x_{m}>和Y=< y1,y2,...,yn y 1 , y 2 , . . . , y n y_{1},y_{2},...,y_{n}>,求X和Y的最长公共公共子序列。(连续子序列必须连续的)
例如:输入两个字符串 acbac和 acaccbabb,则最大连续子串为 “cba”, 则返回长度 3。
动态规划
时间复杂度O(M*N),空间复杂度O(M*N)。
这个 LCS 跟前面说的最长公共子序列的 LCS 不一样,不过也算是 LCS 的一个变体,在 LCS 中,子序列是不必要求连续的,而子串则是 “连续” 的。
我们还是像之前一样 “从后向前” 考虑是否能分解这个问题,类似最长公共子序列的分析,这里,我们使用c[i,j] 表示 以 Xi 和 Yj 结尾的最长公共子串的长度,因为要求子串连续,所以对于 Xi 与 Yj 来讲,它们要么与之前的公共子串构成新的公共子串;要么就是不构成公共子串。故状态转移方程:
代码
public class LCString {public static int lengthofLCString(String X, String Y){/* 构造二维数组c[][]记录X[i]和Y[j]的LCS长度 (i,j)是前缀* c[i][j]=0; 当 i = j = 0;* c[i][j]=c[i-1][j-1]+1; 当 i = j > 0; Xi == Y[i]* c[i][j]=0; 当 i = j > 0; Xi != Y[i]* 需要计算 m*n 个子问题的长度 即 任意c[i][j]的长度* -- 填表过程*/int[][]c = new int[X.length()+1][Y.length()+1];int maxlen = 0;int maxindex = 0;for(int i =1;i<=X.length();i++){for(int j=1;j<=Y.length();j++){if(X.charAt(i-1) == Y.charAt(j-1)){c[i][j] = c[i-1][j-1]+1;if(c[i][j] > maxlen){maxlen = c[i][j];maxindex = i + 1 - maxlen;}}}}return maxlen;}public static void main(String[] args) {String X = "acbac";String Y = "acaccbabb";System.out.println(lengthofLCString(X,Y)); }
}
最长递增子序列(longest-common-subsequence)
问题描述
给定一个序列X=< x1,x2,...,xm x 1 , x 2 , . . . , x m x_{1},x_{2},...,x_{m}>,求X的最长递增子序列。(子序列可以是不连续的,比如{5,6,7,1,2,8} 的LIS是5,6,7,8
动态规划
时间复杂度O(N^2),空间复杂度O(N)。
DP[i]怎么计算?
遍历所有j < i的元素,检查是否DP[j]+1>DP[i] && arr[j] < arry[i] 若是,则可以更新DP[i]
以{5,6,7,1,2,8} 的LIS是5,6,7,8举例:
如何选取DP[5]的值呢?就是在所有8之前的小于8的值(即j < i && arr[j] < arry[i])中,选择dp[j]最大的值,
DP[i] = DP[j] + 1
DP[5] = DP[2] + 1 = 3 + 1 = 4
代码
int maxLength = 1, bestEnd = 0;
DP[0] = 1;
prev[0] = -1;for (int i = 1; i < N; i++)
{DP[i] = 1;prev[i] = -1;for (int j = i - 1; j >= 0; j--)if (DP[j] + 1 > DP[i] && array[j] < array[i]){DP[i] = DP[j] + 1;prev[i] = j;}if (DP[i] > maxLength){bestEnd = i;maxLength = DP[i];}
最长公共子序列、最长连续公共子序列、最长递增子序列相关推荐
- 动态规划设计方法详解最长递增子序列
很多读者反应,就算看了前文动态规划详解,了解了动态规划的套路,也不会写状态转移方程,没有思路,怎么办?本文就借助「最长递增子序列」来讲一种设计动态规划的通用技巧:数学归纳思想. 最长递增子序列(Lon ...
- LeetCode高频题300. 最长递增子序列
LeetCode高频题300. 最长递增子序列 提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目 互联网大厂们在公司养了一大批A ...
- 程序员面试100题之十二:求数组中最长递增子序列
写一个时间复杂度尽可能低的程序,求一个一维数组(N个元素)中最长递增子序列的长度. 例如:在序列1,-1,2,-3,4,-5,6,-7中,其最长递增子序列为1,2,4,6. 分析与解法 根据题目要求, ...
- [51Nod 1218] 最长递增子序列 V2 (LIS)
传送门 Description 数组A包含N个整数.设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS).A的LIS可 ...
- 动态规划作业 最长单调递增子序列
动态规划作业 1.最长单调递增子序列 设计一个 O(n2)时间的算法,找出由 n 个数组成的序列 a 的最长单调递增子序列. 提示: 用数组 b[0:n]纪录以 a[i] (0<= i< ...
- 最大子序列、最长递增子序列、最长公共子串、最长公共子序列、字符串编辑距离
最大子序列 最大子序列是要找出由数组成的一维数组中和最大的连续子序列.比如{5,-3,4,2}的最大子序列就是 {5,-3,4,2},它的和是8,达到最大:而 {5,-6,4,2}的最大子序列是{4, ...
- cstring查找子字符串_动态规划6:两个字符串的最长连续公共子串
本文和前一篇:动态规划5-两个字符串的最长公共子序列类似,但公共子串必须是连续的,子序列不需要连续 字符串a,长度为m:a[1].a[2].a[3].a[4]....a[m] 字符串b,长度为n:b[ ...
- 最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和...
最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和 文章作者:Yx.Ac 文章来源:勇幸|Thinking (http://www.ahathi ...
- 算法设计 - LCS 最长公共子序列最长公共子串 LIS 最长递增子序列
出处 http://segmentfault.com/blog/exploring/ 本章讲解: 1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度: 2. 与之类似但不 ...
最新文章
- 漫画:毕昇 JDK,重现了 “活字印刷术” 的传奇
- 二分图最大匹配 -- 匈牙利算法
- 怎么用python编简单游戏_用Python实现一个简单的算术游戏详解
- 苹果系统更新服务器繁忙,如何解决升级MacOS 10.13时的常见问题
- Tmux的安装、使用与配置
- 大一计算机上机试题2017,2017历年全国计算机二级ACCESS上机试题及答案
- C#设计模式之12-代理模式
- Python中表示自然底数与浮点数精度(等效于MATLAB中eps)
- 面试北京XX数通总结
- 集合竞价如何买入_世界上最稳健的抓涨停方法“10分钟集合竞价”选股诀窍,买入直接稳赚10个点,赚到笑...
- 《软件方法》读书笔记2
- JSP技术-01-语法及运行原理
- 软碟通UltraISO Premium Edition_v9.7.5.3716,轻松编辑光盘镜像文件,将文件/文件夹制作成ISO文件
- 数据的存储------计算机中常见数据类型的存储方式(C语言解析)
- 2021年危险化学品生产单位安全生产管理人员新版试题及危险化学品生产单位安全生产管理人员考试总结
- SAP BAPI是个啥
- 硬盘坏了可以修复吗?硬盘数据恢复方法来了
- 2033-人见人爱A+B(java)
- c语言股价连续上涨的天数,为什么通达信没有连涨天数,我的通达信怎么没有连涨天数显示...
- SpringCloud(7) LCN分布式事务框架入门