这篇日志主要为了记录这几天的学习成果。

最长公共子序列根据要不要求子序列连续分两种情况。

只考虑两个串的情况,假设两个串长度均为n.

一,子序列不要求连续。

(1)动态规划(O(n*n))

(转自:http://www.cnblogs.com/xudong-bupt/archive/2013/03/15/2959039.html

    动态规划采用二维数组来标识中间计算结果,避免重复的计算来提高效率。

    1)最长公共子序列的长度的动态规划方程

    设有字符串a[0...n],b[0...m],下面就是递推公式。字符串a对应的是二维数组num的行,字符串b对应的是二维数组num的列。

    

    另外,采用二维数组flag来记录下标ij的走向。数字"1"表示,斜向下;数字"2"表示,水平向右;数字"3"表示,竖直向下。这样便于以后的求解最长公共子序列。

代码:

 #include<stdio.h>
#include<string.h> char a[],b[];
char num[][]; ///记录中间结果的数组
char flag[][]; ///标记数组,用于标识下标的走向,构造出公共子序列
void LCS(); ///动态规划求解
void getLCS(); ///采用倒推方式求最长公共子序列 int main()
{
int i;
strcpy(a,"ABCBDAB");
strcpy(b,"BDCABA");
memset(num,,sizeof(num));
memset(flag,,sizeof(flag));
LCS();
printf("%d\n",num[strlen(a)][strlen(b)]);
getLCS();
return ;
} void LCS()
{
int i,j;
for(i=;i<=strlen(a);i++)
{
for(j=;j<=strlen(b);j++)
{
if(a[i-]==b[j-]) ///注意这里的下标是i-1与j-1
{
num[i][j]=num[i-][j-]+;
flag[i][j]=; ///斜向下标记
}
else if(num[i][j-]>num[i-][j])
{
num[i][j]=num[i][j-];
flag[i][j]=; ///向右标记
}
else
{
num[i][j]=num[i-][j];
flag[i][j]=; ///向下标记
}
}
}
} void getLCS()
{ char res[];
int i=strlen(a);
int j=strlen(b);
int k=; ///用于保存结果的数组标志位
while(i> && j>)
{
if(flag[i][j]==) ///如果是斜向下标记
{
res[k]=a[i-];
k++;
i--;
j--;
}
else if(flag[i][j]==) ///如果是斜向右标记
j--;
else if(flag[i][j]==) ///如果是斜向下标记
i--;
} for(i=k-;i>=;i--)
printf("%c",res[i]);
}

(2)转化为最长递增子序列问题,O( n*log(n) )

  (转自:http://karsbin.blog.51cto.com/1156716/966387

  注意到num[i][j]仅在A[i]==B[j]处才增加,对于不相等的地方对最终值是没有影响的。故而枚举相等点处可以对上述动态规划算法进行优化。

 

  举例说明:

  A:abdba

  B:dbaaba

  则 1:先顺序扫描A串,取其在B串的所有位置:

  2:a(2,3,5) b(1,4) d(0)。

  3:用每个字母的反序列替换,则最终的最长严格递增子序列的长度即为解。

  替换结果:532 41 0 41 532

  最大长度为3.

  对于一个满足最长严格递增子序列的序列,该序列必对应一个匹配的子串。

  反序是为了在递增子串中,每个字母对应的序列最多只有一个被选出。

  反证法可知不存在更大的公共子串,因为如果存在,则求得的最长递增子序列不是最长的,矛盾。

  最长递增子序列可在O(NLogN)的时间内算出。

二,子序列要求连续

(1) 暴力枚举(O(n^4))

  方法: 枚举B串所有子串,对比确定该子串是否为A串的某一子串,返回最长子串的长度。

  复杂度分析: B串子串个数为O(n^2), 确定子串是否为A 串的一部分,为O(n^2),故而总的复杂度为O(n^4)

(2) KMP优化匹配过程( O(n^3) )

  在算法一中用KMP优化子串与A串的匹配过程,可以将匹配过程优化为线性时间O(n),故而总的复杂度为O(n^3).

(3)  引入KMP( O(n^2) )

  方法: 将B串的所有后缀串(n个),与A串做KMP匹配,返回匹配过程中最长配对长度。 时间复杂度为O(n^2)

(4)  后缀数组解法(O(n*log(n)) )

  (转自:https://www.byvoid.com/blog/lcs-suffix-array

  关于后缀数组的构建方法以及Height数组的性质,本文不再具体介绍,可以参阅IOI国家集训队2004年论文《后缀数组》(许智磊)和IOI国家集训队2009年论文《后缀数组——处理字符串的有力工具》(罗穗骞)。后缀数组可以在线性时间建立起来,DC3.

  回顾一下后缀数组,SA[i]表示排名第i的后缀的位置,Height[i]表示后缀SA[i]和SA[i-1]的最长公共前缀(Longest Common Prefix,LCP),简记为Height[i]=LCP(SA[i],SA[i-1])。连续的一段后缀SA[i..j]的最长公共前缀,就是H[i-1..j]的最小值,即LCP(SA[i..j])=Min(H[i-1..j])。

  求N个串的最长公共子串,可以转化为求一些后缀的最长公共前缀的最大值,这些后缀应分属于N个串。具体方法如下:

  设N个串分别为S1,S2,S3,...,SN,首先建立一个串S,把这N个串用不同的分隔符连接起来。S=S1[P1]S2[P2]S3...SN-1[PN-1]SN,P1,P2,...PN-1应为不同的N-1个不在字符集中的字符,作为分隔符(后面会解释为什么)。

  接下来,求出字符串S的后缀数组和Height数组,可以用倍增算法,或DC3算法。

  然后二分枚举答案A,假设N个串可以有长度为A的公共字串,并对A的可行性进行验证。如果验证A可行,A'(A'<A)也一定可行,尝试增大A,反之尝试缩小A。最终可以取得A的最大可行值,就是这N个串的最长公共子串的长度。可以证明,尝试次数是O(logL)的。

  于是问题就集中到了,如何验证给定的长度A是否为可行解。方法是,找出在Height数组中找出连续的一段Height[i..j],使得i<=k<=j均满足Height[k]>=A,并且i-1<=k<=j中,SA[k]分属于原有N个串S1..SN。如果能找到这样的一段,那么A就是可行解,否则A不是可行解。

  具体查找i..j时,可以先从前到后枚举i的位置,如果发现Height[i]>=A,则开始从i向后枚举j的位置,直到找到了Height[j+1]<A,判断[i..j]这个区间内SA是否分属于S1..SN。如果满足,则A为可行解,然后直接返回,否则令i=j+1继续向后枚举。S中每个字符被访问了O(1)次,S的长度为NL+N-1,所以验证的时间复杂度为O(NL)。

  到这里,我们就可以理解为什么分隔符P1..PN-1必须是不同的N-1个不在字符集中的字符了,因为这样才能保证S的后缀的公共前缀不会跨出一个原有串的范围。

  后缀数组是一种处理字符串的强大的数据结构,配合LCP函数与Height数组的性质,后缀数组更是如虎添翼。利用后缀数组,容易地求出了多个串的LCS,而且时空复杂度也相当优秀了。虽然比起后缀树的解法有所不如,但其简明的思路和容易编程的特点却在实际的应用中并不输于后缀树。

(4)  后缀数(O(n) )

  将A#B$作为字符串压入后缀树,找到最深的非叶节点,且该节点的叶节点既有#也有$(无#)。由于后缀树可以在线性时间建立,而且遍历后缀树需要线性时间(该后缀树中节点数目不大于 2(|A|+|B|)),故而总的时间为线性。

LCS最长公共子序列(最优线性时间O(n))的更多相关文章

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

    出处 http://segmentfault.com/blog/exploring/ 本章讲解:1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度:2. 与之类似但不同的 ...

  2. POJ 1458 Common Subsequence(LCS最长公共子序列)

    POJ 1458 Common Subsequence(LCS最长公共子序列)解题报告 题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?c ...

  3. 动态规划模板2|LCS最长公共子序列

    LCS最长公共子序列 模板代码: #include <iostream> #include <string.h> #include <string> using n ...

  4. LCS 最长公共子序列

    区别最长公共子串(连续) ''' LCS 最长公共子序列 ''' def LCS_len(x, y): m = len(x) n = len(y) dp = [[0] * (n + 1) for i ...

  5. LCS(最长公共子序列问题)

    LCS(Longest Common Subsequence),即最长公共子序列.一个序列,如果是两个或多个已知序列的子序列,且是所有子序列中最长的,则为最长公共子序列. 原理:    事实上,最长公 ...

  6. LCS最长公共子序列

    问题:最长公共子序列不要求所求得的字符串在所给字符串中是连续的,如输入两个字符串ABCBDAB和BDCABA,字符串BCBA和BDAB都是他们的公共最长子序列 该问题属于动态规划问题 解答:设序列X= ...

  7. LCS最长公共子序列HDU1159

    最近一直在学习算法,基本上都是在学习动态规划以及字符串.当然,两者交集最经典之一则是LCS问题. 首先LCS的问题基本上就是在字符串a,b之间找到最长的公共子序列,比如 YAOLONGBLOG 和 Y ...

  8. POJ 2250(LCS最长公共子序列)

    compromise Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u   Descri ...

  9. LCS最长公共子序列~dp学习~4

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1513 Palindrome Time Limit: 4000/2000 MS (Java/Others ...

随机推荐

  1. SQLserver使用映射表进行数据相关操作

    基本需求: 老数据有老数据的顺序编码规则,新数据有新数据的顺序编码规则,但是老数据的编码还是要更新相应的东西,新数据也得实时更新,在新数据中已经用新编码规则对老数据对进行编码,在上报表中既要新增新数据 ...

  2. [原创]从Oracle和Microsoft Sql Server迁移到PostgreSQL Plus Advanced Server

    一.了解PPAS的迁移方式1.在线迁移和离线迁移使用Migration Studio或Migration Toolkit直接向PPAS数据库进行对象定义和数据表中数据的迁移称为在线迁移,生成要迁移对象 ...

  3. swift学习(二)--基本运算符、字符串、集合操作

    在这一篇博客里面,我想要介绍一下swift里面一些常用的基本运算符,还有涉及到的字符串,集合操作.你会发现在swift里面还是有许多其他语言所不具有的特性运算操作的. 首先最基本的+,-,*,/,&g ...

  4. 十一、从头到尾彻底解析Hash 表算法

    在研究MonetDB时深入的学习了hash算法,看了作者的文章很有感触,所以转发,希望能够使更多人受益! 十一.从头到尾彻底解析Hash 表算法 作者:July.wuliming.pkuoliver  ...

  5. [转]Linux下的图形库介绍

    [转]Linux 下的图形库介绍 http://blog.csdn.net/gogor/article/details/5925925 在进行Linux下的图形系统编程时,我们常常会遇到以下这些概念: ...

  6. [转]How do I use software from a PPA

    [转]How do I use software from a PPA? https://launchpad.net/~mc3man/+archive/ubuntu/trusty-media To s ...

  7. ALTERA MAX10官方评估板,新鲜出炉!

    刚刚拿到骏龙提供的ALTERA MAX10官方评估板,还热乎呢,呵呵!赶紧跟大家分享一下 板子很简单,把IO口都扩展出来了,其他功能基本上没有. FPGA型号是10M08SAE144C8GES,144 ...

  8. mozilla css developer center

    https://developer.mozilla.org/en-US/docs/Web/CSS

  9. PSP0表格

    一 项目计划日志 周活动总结表 姓名:陆宇                                                                         日期:3.1 ...

  10. pspo

    一.项目计划总结: 周活动总结表 姓名:               日期:3.12.2015 日期       任务 听课 编写程序 阅读课本 准备考试 日总计 周日 周一 周二 周三 10:00- ...