最长公共子序列(LCS问题)
先简单介绍下什么是最长公共子序列问题,其实问题很直白,假设两个序列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 6 int 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);
else
PRINT_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问题)的更多相关文章
- 1006 最长公共子序列Lcs
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdks ...
- 动态规划之最长公共子序列LCS(Longest Common Subsequence)
一.问题描述 由于最长公共子序列LCS是一个比较经典的问题,主要是采用动态规划(DP)算法去实现,理论方面的讲述也非常详尽,本文重点是程序的实现部分,所以理论方面的解释主要看这篇博客:http://b ...
- 编程算法 - 最长公共子序列(LCS) 代码(C)
最长公共子序列(LCS) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 给定两个字符串s,t, 求出这两个字符串最长的公共子序列的长度. 字符 ...
- C++版 - Lintcode 77-Longest Common Subsequence最长公共子序列(LCS) - 题解
版权声明:本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C++版 - L ...
- POJ 1458 Common Subsequence(最长公共子序列LCS)
POJ1458 Common Subsequence(最长公共子序列LCS) http://poj.org/problem?id=1458 题意: 给你两个字符串, 要你求出两个字符串的最长公共子序列 ...
- 51Nod 1006:最长公共子序列Lcs(打印LCS)
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
- 51nod 1006 最长公共子序列Lcs 【LCS/打印path】
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
- 每日一题-——最长公共子序列(LCS)与最长公共子串
最长公共子序列(LCS) 思路: 代码: def LCS(string1,string2): len1 = len(string1) len2 = len(string2) res = [[0 for ...
- 51nod 1006:最长公共子序列Lcs
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
- 动态规划之最长公共子序列(LCS)
转自:http://segmentfault.com/blog/exploring/ LCS 问题描述 定义: 一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 ...
随机推荐
- 解决Asp.net Mvc返回JsonResult中DateTime类型数据格式的问题
问题背景: 在使用asp.net mvc 结合jquery esayui做一个系统,但是在使用使用this.json方法直接返回一个json对象,在列表中显示时发现datetime类型的数据在转为字符 ...
- [转]linux之more命令
转自:http://www.cnblogs.com/peida/archive/2012/11/02/2750588.html more命令,功能类似 cat ,cat命令是整个文件的内容从上到下显示 ...
- Nginx gzip配置详解
gzip决定是否开启gzip模块param:on|offexample:gzip on; gzip_buffers 设置gzip申请内存的大小,其作用是按块大小的倍数申请内存空间param1:intp ...
- 大数据处理-bitmap是个神马东西
1. Bit Map算法简介 所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素.由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省. 2. B ...
- PropertyPlaceholderConfigurer加载属性配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- 解决g++:command not found(centos7.0)
问题背景,因为装了虚拟机,系统为centos7.0,由于是纯净版,没有gcc,使用命令yum install gcc安装了gcc,但是没安装g++,导致g++:command not found问题. ...
- SpringMVC + Spring + MyBatis 学习笔记:为MyBatis增加打印SQL功能 (最简化配置)
系统:WIN8.1 数据库:Oracle 11GR2 开发工具:MyEclipse 8.6 框架:Spring3.2.9.SpringMVC3.2.9.MyBatis3.2.8 1.以下jar包拷贝到 ...
- 为operamasks增加HTML扩展方式的组件调用
#为operamasks增加HTML扩展方式的组件调用 ##背景 之前的[博文](http://www.cnblogs.com/p2227/p/3540858.html)中有提及到,发现easyui中 ...
- Hadoop入门简介
一.Hadoop简介 1.1.Hadoop主要进行分布式存储和分布式计算 1.1-1.HDFS:分布式文件系统 1.1-2.MapReduce:并行计算框架 1.2.Hadoop用来做什么? 搭建大型 ...
- Hadoop HDFS概念学习系列之分布式文件管理系统(二十五)
数据量越来越多,在一个操作系统管辖的范围存在不了,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,因此迫切需要一种系统来 管理多台机器上的文件,这就是分布式文件管理系统. 是一种允许文件 ...