问题描述:

对于两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列。X  Y   各自字符串有顺序,但是不一定需要相邻。

最长公共子串(Longest Common Substring ):顺序相同,并且各个字符的位置也必须相邻。

最长公共子序列(Longest Common Subsequence,LCS ):顺序形同,各个字符的位置不一定相邻。

比如:

字符串 13455 与 245576 的最长公共子序列为455
字符串 acdfg 与 adfc 的最长公共子序列为adf      adf在acdfg中的顺序相同,但是不相邻。

1:暴力求解

即对X的每一个子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列,并且在检查过程中选出最长的公共子序列。X和Y的所有子序列都检查过后即可求出X和Y的最长公共子序列。X的一个子序列相应于下标序列{1, 2, …, m}的一个子序列,因此,X共有2m个不同子序列(Y亦如此,如为2^n,每个元素都有取或者不取的情况),从而穷举搜索法需要指数时间(2^m * 2^n)。

2:动态规划

先定义一些内容:定义两个字符串X,Y

Xi=<x1,x2.....xi>  表示字符串的 i 前缀

Yj = <y1,y2....yj> 表示字符串y的j前缀

用LCS(Xi,Yj) 表示字符串X,Y的最长公共子序列

如果

  • Xm = Yn(如果字符串X的第m位置上的字符等于字符串Y的第n个位置上的字符(下面类似,不再表述))

则LCS(Xm,Yn) = LCS(Xm-1,Yn-1)+Xm

  • Xm != Yn

则LCS(Xm,Yn) = LCS(Xm-1,Yn)   或者LCS(Xm,Yn)=LCS(Xm,Yn-1)

但是为了追求最大的公共子串则可以这么定义:LCS(Xm,Yn) = max { LCS ( Xm-1 , Yn ) , LCS( Xm , Yn-1 ) }

所以综上两种情况,我们可以得出如下的推导公式:

最后的最长公共子序列 ,为了将问题变成数学问题,可以这么定义上面的过程。

  • 利用一个二维数组表示每一部分LCS的长度  c[m][n]
  • 用c[i][j] 记录字符串Xi和Yj的最长公共子序列长度。

因此可以得到下面的推论:

为了方便表示,我们可以将字符的从1开始。

下面是一个实例图

对于 X = ABCBDAB

Y = BDCABA

下面是具体的代码演示:

 import java.util.Stack;

 /**
* 题目:最长公共子序列。(longest common subsequence)
* 比如字符串a = acdfg b =adfc 则其lcs 为 adf 。
* 思路:可以利用动态规划的思想。
*
* @author Xia
*
*/
public class StringLCS {
public String lsc(String s1,String s2){
int len1 = s1.length();
int len2 = s2.length();
//用一个二维数组表示,这里面为了方便表示将数组行、列都扩大一个(想一想原因,可以结合上面的模式图思考)
int[][] c = new int[len1 + 1][len2 + 1];
//为了配合上面的增加一列,字符串中也应该增加一个字符
s1 = ","+s1;
s2 = "?"+s2; //c中的第一行第一列都赋值为0
int i,j;
for (i = 0; i <= len1; i++) {
c[i][0] = 0;
} for (j = 0; j <= len2; j++) {
c[0][j] = 0;
}
//这里面开始遍历的时候必须从1开始
for ( i = 1; i <= len1; i++) {
for ( j = 1; j <= len2; j++) {
if (s1.charAt(i) == s2.charAt(j)) {
c[i][j] = c[i-1][j-1]+ 1;
}else {
c[i][j] = Math.max(c[i-1][j], c[i][j-1]);
}
}
}
//--------------两层for循环结束后即可得到最长公共子序列的长度 //--------------开始求解最长公共子序列------------------
//从求好的二维数组末端开始向前寻找,那么为了最后能够最长的输出子序列,需要定义一个stack
i = len1;
j = len2;
Stack<Character> s = new Stack<Character>();
while (i != 0 && j != 0) {
if (s1.charAt(i) == s2.charAt(j)) {
s.push(s1.charAt(i));
i--;
j--;
}else {
if (c[i][j-1] > c[i-1][j]) {
j--;
}else {
i--;
}
}
}
//-------------到这一步,已经求好了最长公共子序列并且存储在stack中
StringBuilder sb = new StringBuilder();
while (!s.isEmpty()) {
sb.append(s.pop());
}
return sb.toString();
} }

测试类:

public class Test {
public static void main(String[] args) {
StringLCS lcs = new StringLCS();
String s1 = "acdfg";
String s2 = "adcf";
String result = lcs.lsc(s1, s2);
System.out.println(result);    //acf
}
}

总结:根据最后的结论,多运行几次我们可以看到,其实最长公共子序列是可以有多种情况的。

还会继续更新关于其他的字符串的操作算法演示。

算法练习——最长公共子序列的问题(LCS)的更多相关文章

  1. Java实现 蓝桥杯VIP 算法提高 最长公共子序列

    算法提高 最长公共子序列 时间限制:1.0s 内存限制:256.0MB 问题描述 给定两个字符串,寻找这两个字串之间的最长公共子序列. 输入格式 输入两行,分别包含一个字符串,仅含有小写字母. 输出格 ...

  2. ACM/ICPC 之 最长公共子序列计数及其回溯算法(51Nod-1006(最长公共子序列))

    这道题被51Nod定为基础题(这要求有点高啊),我感觉应该可以算作一级或者二级题目,主要原因不是动态规划的状态转移方程的问题,而是需要理解最后的回溯算法. 题目大意:找到两个字符串中最长的子序列,子序 ...

  3. 【算法】最长公共子序列(nlogn)

    转载注明出处:http://blog.csdn.net/wdq347/article/details/9001005 (修正了一些错误,并自己重写了代码) 最长公共子序列(LCS)最常见的算法是时间复 ...

  4. 算法实践--最长公共子序列(Longest Common Subsquence)

    什么是最长公共子序列 X=ACCG Y=CCAGCA 长度为1的公共子序列: {A} {C} {G} 长度为2的公共子序列:{AC} {CC} {CG} {AG} 长度为3的公共子序列:{ACG} 长 ...

  5. P2516 [HAOI2010]最长公共子序列 题解(LCS)

    题目链接 最长公共子序列 解题思路 第一思路: 1.用\(length[i][j]\)表示\(a\)串的前\(i\)个字符与\(b\)串的前\(j\)个字符重叠的最长子串长度 2.用\(num[i][ ...

  6. 最长公共子序列问题(LCS)——Python实现

      # 最长公共子序列问题 # 作用:求两个序列的最长公共子序列 # 输入:两个字符串数组:A和B # 输出:最长公共子序列的长度和序列 def LCS(A,B): print('输入字符串数组A', ...

  7. 最长公共子序列问题(LCS) 洛谷 P1439

    题目:P1439 [模板]最长公共子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 关于LCS问题,可以通过离散化转换为LIS问题,于是就可以使用STL二分的方法O(nlogn ...

  8. 《算法导论》读书笔记之动态规划—最长公共子序列 & 最长公共子串(LCS)

    From:http://my.oschina.net/leejun2005/blog/117167 1.先科普下最长公共子序列 & 最长公共子串的区别: 找两个字符串的最长公共子串,这个子串要 ...

  9. 程序员的算法课(6)-最长公共子序列(LCS)

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/de ...

随机推荐

  1. ecmascript 6 的arguments转数组的代码

  2. Linux 网络流量查看 Linux ip traffic monitor

    Network monitoring on Linux This post mentions some linux command line tools that can be used to mon ...

  3. 数组转换list集合问题

    1问题的起因: 项目总要使用一个方法把数组转换成list集合,百度说有这个方法Arrays.asList可以实现 2结果报错 抛出java.lang.UnsupportedOperationExcep ...

  4. C# 标准的MD5加密32位

    标准的MD5加密32位小写的: public static string GetMD5(string myString) { MD5 md5 = new MD5CryptoServiceProvide ...

  5. 借助System.Linq.Dynamic, IQueryable根据排序字符串排序

    在使用Entity Framework时,若有多个排序,需要OrderBy (OrderByDescending)再ThenBy (ThenByDescending) 假设需要根据Name升序排序,再 ...

  6. Oracle数据库运维:要对监听日志文件(listener.log)进行定期清理,如果不定期清理,会遇到下面一些麻烦

    原文链接: http://www.lookdaima.com/WebForms/WebPages/Blanks/Pm/Docs/DocItemDetail.aspx?EmPreviewTypeV=2& ...

  7. September 15th 2017 Week 37th Friday

    First I need your hand, then forever can begin. 我需要牵着你的手,才能告诉你什么是永远. If you want to shake hands with ...

  8. 如何处理高并发情况下的DB插入

    1.  我们需要接收一个外部的订单,而这个订单号是不允许重复的 2.  数据库对外部订单号没有做唯一性约束 3.  外部经常插入相同的订单,对于已经存在的订单则拒绝处理 对于这个需求,很简单我们会用下 ...

  9. SOJ 4580 动态规划之01背包 (01背包)

    Description Sidney想去Gandtom家玩.但Sidney家和Gandtom家之间是高低不平.坑坑洼洼的土路.所以他需要用他的背包装几袋稀的泥,在路上铺平一些干的土,使路变成平整的泥土 ...

  10. OOP——构造函数、析构函数

    我们在创建和销毁对象时需要执行一些任务.例如,在创建对象时给属性赋值,在对象销毁时关闭数据连接等,这时就需要构造函数和析构函数. 在PHP中构造函数和析构函数是固定的,如下: // 构造函数 func ...