先简单介绍下什么是最长公共子序列问题,其实问题很直白,假设两个序列X,Y,X的值是ACBDDCB,Y的值是BBDC,那么XY的最长公共子序列就是BDC。这里解决的问题就是需要一种算法可以快速的计算出这个最大的子序列,当然,用最简单的方法就是列出XY全部的子系列然后一个个对比,但这样的时间复杂度是绝对不能接受的。假设X的长度是m,Y的长度是n,拿X的一个子序列和Y进行对比的时间是n,计算X的全部子序列的时间是2^m,所以,如果采用的是一个个全部计算的话,将会花费n*2^m的时间,指数级别的时间复杂度是爆炸式的。我们这里解决的方法是采用动态规划的方式,所以再讲问题之前,先简单提下动态规划的概念。

动态规划

动态规划是通过组合子问题的解而解决整个问题的,说到这里,是不是有些熟悉?在先前的归并排序中采用的分治法其实也是对子问题进行分析,但有所不同的是,分治法的思想是通过将问题分解为多个子问题,然后一一解决,最后合并子问题就得到了原问题的答案。当然,分治法所适用的领域就是子问题没有相互的关联,而动态规划所就没那么简单了,它的子问题一般都是由相互关联的情况,也就是说子问题包含了公共的子子问题。什么叫公共的子子问题,就是一个子问题继续分解,另一个子问题也继续分解,然后它们惊讶的发现它们分解出来的问题竟然是一样的。所以假设使用分治法来计算这种问题的话,就会产生许多不必要的重复计算,而动态规划的目标之一就是去除这种重复的计算,而方法就是讲结果放在一张表中,具体的怎么弄可以详细看最长公共子序列问题怎么解的。

动态规划常常用于最优解问题,具体的设计可以参考下面的部分:

1.描述最优解的结构

2.递归定义最优解的值

3.按自底向上的方式计算最优解的值

4.由计算结果构造一个最优解

这几个步骤等等就会被用于求解最长公共子序列的问题上,具体步骤请对号入座。

最长公共子序列可以用来干嘛?

在解决这个问题之前,我们当然需要刚清楚自己算出来的东西有什么用处吧~就直接说书上的例子吧,生物里面的DNA由ACGT四种碱基构成,两种生物的DNA序列就是这四种碱基的集合,而有时候就是需要检测两种生物DNA的近似度,一般采用的方法比较多,可以检测一种生物的DNA是不是另一种生物DNA的子集,或者另一种方式,就是如果有第三条DNA序列同时出现在前面两条DNA之中,DNA出现的序列顺序必须相同,但并不是一定要连续出现。所以,寻找到的那个公共的DNA序列越长,那么两个生物DNA就越近似。

应用先将这么多,首先需要详细定义最大公共子序列问题。

一个给定序列的子序列就是该给定序列中去掉零个或者多个元素。另一种形式化的定义(看不看无所谓,看懂开篇说明的话就可以略过),给定一个序列X=<x1,x2,...,xm>,另一个序列Z=<z1,z2,...,zk>是X的一个子序列,如果存在X的一个严格递增下标序列<i1,i2,...,ik>,使得对所有的j=1,2,...,k有xij=zj(注意这里左边j是i的下标)。

给定两个序列X和Y,Z是X和Y的公共子序列,假若Z的长度是所有的X和Y的公共子序列中最长的,那么称Z为最长公共子序列,算法的目的就是为了求解这些最长公共子序列(注意,Z并不一定唯一)

步骤一:描述一个最长公共子序列

对于以下的最长公共子序列问题,简称为LCS问题。之前提到的将XY中所有的子序列全部计算出来再进行比较显然是不现实的,但是,对于LCS问题而言,具有最优子结构特征,具体的说明在下面的定理中。这里,我们先给出前缀的概念,具体定义就不提了,就举一个例子,假设X=<A,B,C,B,D,A,B>,那么X4=<A,B,C,B>就是X的一个前缀,显然,X0为空。而下面分解的子问题其实就是分解为前缀。

定理(LCS的最优子结构):

设X=<x1,x2,...,xm>,Y=<y1,y2,...,yn>为两个序列,并且Z=<z1,z2,...,zk>为X和Y的任意一个LCS

1.如果xm=yn,那么zk=xm=yn而且Z(k-1)是X(m-1)和Y(n-1)的一个LCS

2.如果xm!=yn,那么zk!=xm蕴含Z是X(m-1)和Y的一个LCS

3.如果xm!=yn,那么zk!=yn蕴含Z是X和Y(n-1)的一个LCS

这些性质的作用其实就是为了说明两个序列的LCS包含了两个序列前缀的LCS,那么LCS就具有最优子结构性质(最优子结构就是一个最优解包含了子结构的最优解)。

步骤二:一个递归解

从上面的定理我们可以知道,在寻找LCS的时候,一般就是从子序列中寻找LCS,这样就要检查一个或者两个字问题。如果xm=yn,那么就要找出X(m-1)和Y(n-1)的LCS,然后将xm=yn加上去,就是X和Y的LCS了。但如果xm!=yn的话,那么就要分别计算X(m-1)和Y以及X和Y(n-1)的两个LCS,比价长的LCS就是要找的。这样虽然表面上是递归分治的问题,但实际上如果仔细观察的话就会发现LCS问题中的重叠子问题,比如对于X(m-1)和Y的子问题,和X和Y(n-1)的子问题中,同时包含了X(m-1)和Y(n-1)的子子问题,这样就导致了重复计算的问题,这样使用分治直接解决就不适用了。具体的解决方法后文将会描述。

要求出最长的子序列,首先要知道的是最长有多长。这里定义C[i,j]为序列Xi和Yj的一个LCS的长度,可得到递归式:

步骤三:计算LCS的长度

其实就是根据上面的递归式给出程序,这里先给出伪代码,具体程序最后给出:

可以看到,在程序中有两个二维数组c和b,c用来记录最长子序列的长度,b的话相当于记录了轨迹,在最后构造LCS的时候起到作用。

下图展示的就是程序运行后表格c和b的具体情形(画在了一张表里)

步骤四:构造一个LCS

只要得到了表b,就可以快速构造出LCS,伪代码如下:

这是一个递归算法,一开始i和j的值为m和n,就是从表格的右下角开始回溯,从而得到LCS。

下面给出具体的C语言实现过程:

#include <stdio.h>
#include <stdlib.h>#define m 7
#define n 6int c[m+1][n+1];
char b[m+1][n+1];void LCS_LENGTH(char* X,char* Y)
{int i,j;for(i=1;i<=m;i++)c[i][0]=0;for(j=0;j<=n;j++)c[0][j]=0;for(i=1;i<=m;i++){for(j=1;j<=n;j++){if(X[i]==Y[j]){c[i][j]=c[i-1][j-1]+1;b[i][j]='\\';}else{if(c[i-1][j]>=c[i][j-1]){c[i][j]=c[i-1][j];b[i][j]='|';}else{c[i][j]=c[i][j-1];b[i][j]='-';}}}}
}void PRINT_LCS(char* X,int i,int j)
{if(i==0 || j==0)return;if(b[i][j]=='\\'){PRINT_LCS(X,i-1,j-1);printf("%c ",X[i]);}else if(b[i][j]=='|')PRINT_LCS(X,i-1,j);elsePRINT_LCS(X,i,j-1);
}int main(void)
{int i,j;char X[m+1]={'X','A','B','C','B','D','A','B'};char Y[n+1]={'Y','B','D','C','A','B','A'};LCS_LENGTH(X,Y);printf("LCS长度表c打印出来是这个样子:\n");for(i=1;i<=m;i++){for(j=1;j<=n;j++)printf("%d ",c[i][j]);printf("\n");}printf("路径表b打印出来是这个样子\n");for(i=1;i<=m;i++){for(j=1;j<=n;j++)printf("%c ",b[i][j]);printf("\n");}printf("\nLCS的具体值是:\n");PRINT_LCS(X,m,n);return 0;
}

当然了,改进代码的方式也有很多,对空间的改进可以完全不使用表b等等,这里就不再详细叙述了。

最长公共子序列(LCS问题)相关推荐

  1. 动态规划算法解最长公共子序列LCS问题

    动态规划算法解LCS问题 作者 July 二零一零年十二月三十一日 本文参考:微软面试100题系列V0.1版第19.56题.算法导论.维基百科. 第一部分.什么是动态规划算法 ok,咱们先来了解下什么 ...

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

    最长公共子序列(LCS,Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最 ...

  3. 程序员编程艺术第十一章:最长公共子序列(LCS)问题

    程序员编程艺术第十一章:最长公共子序列(LCS)问题 0.前言 程序员编程艺术系列重新开始创作了(前十章,请参考程序员编程艺术第一~十章集锦与总结).回顾之前的前十章,有些代码是值得商榷的,因当时的代 ...

  4. 算法之最长公共子序列(LCS)问题

    算法课上老师留的作业,最长公共子序列LCS(Longest Common Subsequence)问题,首先看到这个问题感觉有点复杂,和最长公共子串不同,公共子序列并不要求元素相邻,看起来只有穷举才能 ...

  5. 最长公共子序列php,动态规划(最长公共子序列LCS)

    概念 求解决策过程最优化的结果 (可能有多个) 把多阶段过程转化为一系列单阶段过程,利用各阶段之间的关系,逐个求解 计算过程中会把结果都记录下,最终结果在记录中找到. 举例 求两个字符串的最长公共子序 ...

  6. python实现求解最长公共子序列LCS问题

    在实现论文<Automatically Generating Models for Botnet Detection>论文的算法中,用到了一个The longest commom subs ...

  7. 算法导论-----最长公共子序列LCS(动态规划)

    目录 一.概念梳理 二.最长公共子序列解决方案 方案1:蛮力搜索策略 方案2:动态规划策略 三.C代码实现 实现1 实现2(空间优化) 一.概念梳理   1. 子序列(subsequence): 一个 ...

  8. 动态规划表格法解决最长公共子序列(LCS)问题

    3.5 最长公共子序列(LCS) 前言:图片是博主自己画的,转载请注明出处哦 3.5.1 问题描述 最长公共子序列(Longest Common Subseuence,LCS)问题:给定两个字符串,求 ...

  9. 最长公共子序列 (LCS) 详解+例题模板(全)

    欢迎访问https://blog.csdn.net/lxt_Lucia-- 宇宙第一小仙女\(^o^)/-萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗- ------------ ...

  10. 相似度:最长公共子序列--LCS

    一.概念 1.子序列 一个特定序列的子序列就是将给定序列中零个或多个元素去掉后得到的结果(不改变元素间相对次序).如序列[A,B,C,B,D,A,B]的子序列有:[A,B],[B,C,A],[A,D, ...

最新文章

  1. 压力变动力,存储追求高效率
  2. 如何提取cocos iOS应用程序APP与游戏安装包里的资源与文件
  3. php无法加载Memcache缓存模块问题及Memcache的安装
  4. Oracle EBS PO 接受入库
  5. html移动滚动彩字字幕特效,如何制作滚动字幕 旋转好莱坞字幕特效效果图(超多滚动效果)...
  6. 如何将cv::Mat类型转换为imgui中的ImTextureID类型
  7. LInux下Ubuntu下查看端口占用及关闭
  8. 一天一模式:Decorator模式
  9. Python os.system(command),这样执行的command命令,和主程序是异步的吗?
  10. 一周文章导读:在线试用 Linux 系统;Shell实践;VFS;Makefile陷阱;Shell陷阱
  11. 高通Audio缩写(不断更新中...)
  12. Oracle instr用法
  13. XSS跨站点脚本攻击解决方案
  14. 双层pdf解析 java_java转双层pdf
  15. 存储容量(空间)换算公式
  16. 小白linux学习[1]__虚拟机NAT方式共享电信拨号上网
  17. 如何在linux系统中快速切换目录(目录栈的使用),如何在Linux下环境下快速切换工作目录...
  18. 计算机教案封面设计,四年级信息技术《精彩封面巧设计》教学设计
  19. MUI初体验 模仿微信页面
  20. 在C和C++定义最大最小整数值

热门文章

  1. ubuntu12.04下android开发环境搭建两个注意事项
  2. 基于XMPP协议的aSmack源码分析
  3. MYSQL 设计数据结构注意事项
  4. 一起谈.NET技术,Visual Studio 2010 中的代码约定设置
  5. java美元兑换,(Java实现) 美元汇率
  6. 【转载】Yield,迭代器,生成器
  7. netstat获取本机监听的地址列表 —— *链表实现*(sudo netstat -nl | grep -w tcp | awk '{print $4}')
  8. Tensorflow基础-mnist数据集
  9. Java创建线程的方式
  10. Java 反射机制学习资料