搜档网
当前位置:搜档网 › 算法系列之六:最长公共子序列(LCS)问题(连续子序列)的三种解法

算法系列之六:最长公共子序列(LCS)问题(连续子序列)的三种解法

算法系列之六:最长公共子序列(LCS)问题(连续子序列)的三种解法
算法系列之六:最长公共子序列(LCS)问题(连续子序列)的三种解法

算法系列之六:最长公共子序列(LCS)问题(连续子序列)的三种解法

最长公共子序列(LCS)问题有两种方式定义子序列,一种是子序列不要求不连续,一种是子序列必须连续。上一章介绍了用两种算法解决子序列不要求连续的最终公共子序列问题,本章将介绍要求子序列必须是连续的情况下如何用算法解决最长公共子序列问题。

仍以上一章的两个字符串“abcdea”和“aebcda”为例,如果子序列不要求连续,其最长公共子序列为“abcda”,如果子序列要求是连续,则其最长公共子序列应为“bcd”。在这种情况下,有可能两个字符串出现多个长度相同的公共子串,比如“askdfiryetd”和“trkdffirey”两个字符串就存在两个长度为3的公共子串,分别是“kdf”和“fir”,因此问题的性质发生了变化,需要找出两个字符串所有可能存在公共子串的情况,然后取最长的一个,如果有多个最长的公共子串,只取其中一个即可。

字符串“abcdea”和“aebcda”如果都以最左端的a字符对齐,则能够匹配的最长公共子串就是“a”。但是如果用第二个字符串的e字符对齐第一个字符串的a 字符,则能够匹配的最长公共子串就是“bcd”。可见,从两个字符串的不同位置开始对齐匹配,可以得到不同的结果,因此,本文采用的算法就是穷举两个字符串所有可能的对齐方式,对每种对齐方式进行字符的逐个匹配,找出最长的匹配子串。

一、递归方法

首先看看递归方法。递归的方法比较简单,就是比较两个字符串的首字符是否相等,如果相等则将其添加到已知的公共子串结尾,然后对两个字符串去掉首字符后剩下的子串继续递归匹配。如果两个字符串的首字符不相等,则用三种对齐策略分别计算可能的最长公共子串,然后取最长的一个与当前已知的最长公共子串比较,如果比当前已知的最长公共子串长就用计算出的最长公共子串代替当前已知的最长公共子串。第一种策略是将第一个字符串的首字符删除,将剩下的子串与第二个字符串继续匹配;第二种策略是将第二个字符串的首字符删除,将剩下的子串与第一个字符串继续匹配;第三种策略是将两个字符串的首字符都

删除,然后继续匹配两个字符串剩下的子串。删除首字符相当于字符对齐移位,整个算法实现如下:

180void RecursionLCS(const std::string& str1,const std::string& st r2,std::string& lcs)

181{

182if(str1.length()==0|| str2.length()==0)

183return;

184

185if(str1[0]== str2[0])

186{

187 lcs += str1[0];

188 RecursionLCS(str1.substr(1), str2.substr(1), lcs);

189}

190else

191{

192 std::string strTmp1,strTmp2,strTmp3;

193

194 RecursionLCS(str1.substr(1), str2, strTmp1);

195 RecursionLCS(str1, str2.substr(1), strTmp2);

196 RecursionLCS(str1.substr(1), str2.substr(1), strTmp3); 197 std::string

strLongest = GetLongestString(strTmp1, strTmp2, strTmp3);

198if(lcs.length()< strLongest.length())

199 lcs = strLongest;

200}

201}

二、两重循环方法

使用两重循环进行字符串的对齐匹配过程如下图所示:

图(1)两重循环字符串对齐匹配示意图

第一重循环确定第一个字符串的对齐位置,第二重循环确定第二个字符串的对齐位置,每次循环确定一组两个字符串的对齐位置,并从此对齐位置开始匹配两个字符串的最长子串,如果匹配到的最长子串比已知的(由前面的匹配过程找到的)最长子串长,则更新已知最长子串的内容。两重循环的实现算法如下:

153void LoopLCS(const std::string& str1,const std::string& str2, s td::string&lcs)

154{

155 std::string::size_type i,j;

156

157for(i =0; i < str1.length(); i++)

158{

159for(j =0; j < str2.length(); j++)

160{

161 std::string

lstr = LeftAllignLongestSubString(str1.substr(i),str2.substr(j));

162if(lstr.length()> lcs.length())

163 lcs = lstr;

164}

165}

166}

其中LeftAllignLongestSubString()函数的作用就是从某个对齐位置开始匹配最

长公共子串,其实现过程就是逐个比较字符,并记录最长子串的位置信息。三、改进后的算法

使用两重循环的算法原理简单,LoopLCS()函数的实现也简单,时间复杂度为O(n2)(或O(mn)),比前一个递归算法的时间复杂度O(3n)要好很多。但是如果仔细观察图(1)所示的匹配示意图,就会发现这个算法在m x n次循环的过程中对同一位置的字符进行多次重复的比较。比如i=1,j=0的时候,从对齐位置开始第二次比较会比较第一个字符串的第三个字符“c”与第二个字符串的第二个字符“e”,而在i=1,j=0的时候,这个比较又进行了一次。全部比较的次数可以近似计算为mn(n-1)/2(其中m和n分别为两个字符串的长度),也就是说比较次数是O(n3)数量级的。而理论上两个字符串的不同位置都进行一次比较只需要mn次比较即可,也就是说比较次数的理论值应该是O(n2)数量级。

考虑对上述算法优化,可以将两个字符串每个位置上的字符的比较结果保存到一张二维表中,这张表中的[i,j]位置就表示第一个字符串的第i个字符与第二个字符串的第j个字符的比较结果,1表示字符相同,0表示字符不相同。在匹配最长子串的过程中,不必多次重复判断两个字符是否相等,只需从表中的[i,j]位置直接得到结果即可。

改进后的算法分成两个步骤:首先逐个比较两个字符串,建立关系二维表,然后用适当的方法搜索关系二维表,得到最长公共子串。第一个步骤比较简单,算法的改进主要集中在从关系二维表中得到最长公共子串的方法上。根据比较的原则,公共子串都是沿着二维表对角线方向出现的,对角线上连续出现1就表示这个位置是某次比较的公共子串。有上面的分析可知,只需要查找关系二维表中对角线上连续出现的1的个数,找出最长的一串1出现的位置,就可以得到两个字符串的最长公共子串。改进后的算法实现如下:

105void RelationLCS(const std::string& str1,const std::string& str

2, std::string&lcs)

106{

107int d[MAX_STRING_LEN][MAX_STRING_LEN]={0};

108int length =0;

109

110 InitializeRelation(str1, str2, d);

111int pos = GetLongestSubStringPosition(d, str1.length(), str2 .length(),&length);

112 lcs = str1.substr(pos, length);

113}

InitializeRelation()函数就是初始化二维关系表,根据字符比较的结果将d[i,j]相应的位置置0或1,本文不再列出。算法改进的关键在GetLongestSubStringPosition()函数中,这个函数负责沿对角线搜索最长公共子串,并返回位置和长度信息。仍然以字符串“abcdea”和“aebcda”为例,InitializeRelation()函数计算得到的关系表如图(2)所示:

图(2)示例字符串的位置关系示意图

从图(2)中可以看到,最长子串出现在红线标注的对角线上,起始位置在第一个字符串(纵向)中的位置是2,在第二个字符串(横向)中的位置是3,长度是3。搜索对角线从两个方向开始,一个是沿着纵向搜索左下角方向上的半个关系矩阵,另一个是沿着横向搜索右上角方向上的半个关系矩阵。对每个对角线分别查找连续的1出现的次数和位置,并比较得到连续1最多的位置。GetLongestSubStringPosition()函数的代码如下:

63int GetLongestSubStringPosition(int d[MAX_STRING_LEN][MAX_STRING_ LEN],int m,int n,int*length)

64{

65int k,longestStart,longs;

66int longestI =0;

67int longi =0;

68

69for(k =0; k < n; k++)

70{

71 longi = GetLongestPosition(d, m, n,0, k,&longs);

72if(longi > longestI)

73{

74 longestI = longi;

75 longestStart = longs;

76}

77}

78for(k =1; k < m; k++)

79{

80 longi = GetLongestPosition(d, m, n, k,0,&longs);

81if(longi > longestI)

82{

83 longestI = longi;

84 longestStart = longs;

85}

86}

87

88*length = longestI;

89return longestStart;

90}

GetLongestPosition()函数就是沿着对角线方向搜索1出现的位置和连续长度,算法简单,本文不再列出。

至此,本文介绍了三种要求子串连续的情况下的求解最长公共子串的方法,都是简单易懂的方法,没有使用复杂的数学原理。第一种递归方法的时间复杂度是O(3n),这个时间复杂度的算法在问题规模比较大的情况下基本不具备可用性, 第三种方法是相对最好的方法,但是仍有改进的余地,比如使用位域数组,可以减少存储空间的使用,同时结合巧妙的位运算技巧,可以极大地提高GetLongestPosition()函数的效率。

最长公共子序列问题(最)

算法作业: LCS 问 题 作业要求:设计一个算法求出两个序列的所有LCS ,分析最坏情况,用“会计方法”证明利用b[i][j]求出 所有LCS 的算法在最坏情况下时间复杂度为)(m m n C O + 1、 算法思路: 根据最长公共子序列问题的性质,即经过分解后的子问题具有高度重复性,并且具有最优子结构性质,采用动态规划法求解问题。设X={x 1, x 2, … , x n }, Y={y 1, y 2, … , y m }, 首先引入二维数组C[i][j]记录X i 和Y j 的LCS 的长度,定义C[i][j]如下: { j i j y i 且x ,i,j ]][j C[i j y i x j i j i C j i C j i C 00001110,]},1][[],][1[max{]][[===>+--≠>--=或,且 为了构造出LCS ,还需要使用一个二维数组b[m][n],b[i][j]记录C[i][j]是通过哪个子问题的值求得 的,以决定搜索的方向,欲求出所有的LCS ,定义数组b 如下: 设1-对角线方向;2-向上;3-向左;4-向上或向左 若X[i]=Y[j],b[i][j] = 1, 若C[i-1][j][i][j-1], 则b[i][j] = 3, 若C[i-1][j]=[i][j-1], 则b[i][j] = 4, 根据以上辅助数组C 和b 的定义,算法首先需要求出这两个数组, C[m][n]中记录的最长公共子序列的长度,b 中记录了查找子序列元素的搜索方向。 利用C 和b 的信息,Find_All_LCS 可以采用回溯法求出所有的LCS 。基本思路如下:使用一个辅助数组记录每次调用Find_All_LCS 得到的LCS 中的元素,每次递归调用一次Find_All_LCS ,进入一个新的执行层,首先要判断当前处理的两个子序列长度是否大于等于0 ,若不满足,则该层的递归结束,返回上一层;然后再判断当前得到的子序列是否等于数组C 中求出的最长公共子序列长度,若等于,则说明算法执行到此已经得到一个LCS ,按序输出;若不等于,此时根据数组b 中记录的搜索方向继续搜索,特别要说明的是,当b[i][j]=4时,即要向上或向左,需要对这两个方向分别调用Find_All_LCS ,保证沿着这两个方向上LCS 元素不被漏掉,都可以搜索到;若b[i][j]=1,即沿对角线方向搜索前进时,此时元素X[i]为LCS 中的元素,存放至辅助数组中去,同时将当前已经求得的LCS 长度增1,当递归调用Find_All_LCS 从b[i][j]=1处时,需要回溯一步,搜索其它路径上可能为LCS 中的元素。当所有的可能路径都已经搜索完,算法结束。 对于某些情况会输出重复的LCS ,这是因为算法在沿不同路径搜索时可能会出现相同的LCS 序列。 2、 时间复杂度分析 由上述对Find_All_LCS 算法的分析可知,求出所有的LCS 实际上是根据搜索的方向信息遍历所有的路径找出满足条件的元素集合。因此,除求解辅助数组C 和b 所用的O(mn+m+n)的执行时间外,Find_All_LCS 的时间复杂度取决于所遍历路径数。而路径数是由搜索方向决定的。显然算法在最好的情况下,即m=n 并且b 中所有的值都指示沿着对角线方向搜索,时间复杂度为O(n). 相反,当X 和Y 序列不存在公共子序列时为算法的最坏情况,此时C 中所有值都等于0,数组b 中所有的值都指示要分别沿两个不同的方向(向左或向上)搜索,这种情况下每处理一次X[i],Y[j]时总是要沿两个方向分别调用Find_All_LCS ,遇到i=0或j=0时返回,直到搜索完所有的可能路径才结束,最坏情况下的搜索矩阵如下图所示:

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

程序员编程艺术第十一章:最长公共子序列(LCS)问题 0、前言 程序员编程艺术系列重新开始创作了(前十章,请参考程序员编程艺术第一~十章集锦与总结)。回顾之前的前十章,有些代码是值得商榷的,因当时的代码只顾阐述算法的原理或思想,所以,很多的与代码规范相关的问题都未能做到完美。日后,会着力修善之。 搜遍网上,讲解这个LCS问题的文章不计其数,但大多给读者一种并不友好的感觉,稍感晦涩,且代码也不够清晰。本文力图避免此些情况。力保通俗,阐述详尽。同时,经典算法研究系列的第三章(三、dynamic programming)写的极其糟糕,所以,也算是对那文的一种弥补。有任何问题,欢迎不吝赐教。 第一节、问题描述 什么是最长公共子序列呢?好比一个数列S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则S称为已知序列的最长公共子序列。 举个例子,如:有两条随机序列,如1 3 4 5 5 ,and 2 4 5 5 7 6,则它们的最长公共子序列便是:4 5 5。 第二节、LCS问题的解决思路 ?穷举法 解最长公共子序列问题时最容易想到的算法是穷举搜索法,即对X的每一个子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列,并且在检查过程中选出最长的公共子序列。X和Y的所有子序列都检查过后即可求出X和Y的最长公共子序列。X的一个子序列相应于下标序列{1, 2, …, m}的一个子序列,因此,X共有2m个不同子序列(Y亦如此,如为2^n),从而穷举搜索法需要指数时间(2^m * 2^n)。 ?动态规划算法 事实上,最长公共子序列问题也有最优子结构性质。 记: Xi=﹤x1,?,xi﹥即X序列的前i个字符(1≤i≤m)(前缀) Yj=﹤y1,?,yj﹥即Y序列的前j个字符(1≤j≤n)(前缀) 假定Z=﹤z1,?,zk﹥∈LCS(X , Y)。

求最长子序列的长度

一,最长递增子序列问题的描述 设L=是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=,其中k1是对序列L=按递增排好序的序列。那么显然X与L的最长公共子序列即为L的最长递增子序列。这样就把求最长递增子序列的问题转化为求最长公共子序列问题LCS了。 最长公共子序列问题用动态规划的算法可解。设Li=< a1,a2,…,a i>,Xj=< b1,b2,…,b j>,它们分别为L和X的子序列。令C[i,j]为Li与Xj的最长公共子序列的长度。则有如下的递推方程: 这可以用时间复杂度为O(n2)的算法求解,由于这个算法上课时讲过,所以具体代码在此略去。求最长递增子序列的算法时间复杂度由排序所用的O(nlogn)的时间加上求LCS的O(n2)的时间,算法的最坏时间复杂度为O(nlogn)+O(n2)=O(n2)。 三,第二种算法:动态规划法 设f(i)表示L中以a i为末元素的最长递增子序列的长度。则有如下的递推方程: 这个递推方程的意思是,在求以a i为末元素的最长递增子序列时,找到所有序号在L前面且小于a i的元素a j,即j

(完整word版)最长公共子序列长度算法

// KSY.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include using namespace std; void LCSLength(int m,int n,char *x ,char *y, int **c, int **b) { int i ,j; for (i = 1; i <= m; i++) c[i][0] = 0; for (i = 1; i <= n; i++) c[0][i] = 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]=1; } else if (c[i-1][j]>=c[i][j-1]) { c[i][j]=c[i-1][j]; b[i][j]=2; } else { c[i][j]=c[i][j-1]; b[i][j]=3; } } } void LCS(int i ,int j, char *x ,int **b) { if (i ==0 || j==0) return; if (b[i][j]== 1) { LCS(i-1,j-1,x,b); printf("%c",x[i]); } else if (b[i][j]== 2) LCS(i-1,j,x,b); else LCS(i,j-1,x,b); } const int M = 6; const int N = 5; void output(char *s,int n); void LCSLength(int m,int n,char *x,char *y,int * *c,int * *b); void LCS(int i,int j,char *x,int * *b); void main() { char x[] = {' ','B','C','E','F','G','T'}; char y[] = {' ','C','D','F','J','G'}; int **c = new int *[M+1]; int **b = new int *[M+1]; for(int i=0;i<=M;i++) { c[i] = new int[N+1]; b[i] = new int[N+1]; } cout<<"序列X:"<

最长公共子序列实验报告

河北地质大学课程设计报告 (学院)系: 信息工程学院 专业: 计算机科学与技术 姓名: 李义 班级: 二班 学号: 515109030227 指导教师: 王培崇 2016年11月26 日

算法课程设计报告 姓名李义学号515109030227 日期2016/11/10-2016/12/1 实验室152 指导教师王培崇设备编号08 设计题目求最长公共子序列 一、设计内容 求最长公共子序列,如输入字符串str1=adadsda,str2=sadasfda。 则求出的最长公共子序列是adasda。 二、设计目的 掌握动态规划思想,对使用求最长公共子序列加深理解。 三、设计过程 1.算法设计 1. for i ←0 to n 2. L[i,0] ←0 3. end for 4. for j ←0 to m 5. L[0,j] ←0 6. end for 7. for i ←1 to n 8. for j ←1 to m 9. if ai=bj then L[i,j]←L[i-1,j-1]+1 10. else L[i,j]←max {L[i,j-1], L[i-1,j] } 11. end if 12. end for 13. end for 14. return L[n,m] 2.流程图

开始结束 输入I=0,j=0 i<=n L[I,0]=0 i++ Y L[0,j]=0 N j<=n j++ Y i=1 to n J=1 to m ai=bj L[i,j]=L[i-1,j-1]+1 L[i,j]=max{L[i-1,j ],L[i,j-1]} Y J++i++ N 图1.Lcs 算法 3.数据结构 str1=adadsda str2=sadasfda 四、程序实现及运行结果

动态规划解最长公共子序列问题

动态规划解最长公共子序列问题 动态规划主要针对最优化问题,它的决策是全面考虑不同的情况分别进行决策,,最后通过多阶段决策逐步找出问题的最终解.当各个阶段采取决策后,会不断决策出新的数据,直到找到最优解.每次决策依赖于当前状态,又随机引起状态的转移.一个决策序列就是在变化的状态中产生出来的,故有”动态”的含义.所以,这种多阶段最优化决策解决问题的过程称为动态规划. 一问题的描述与分析 字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干字符(可能一个也不去掉)后形成的字符序列..令给定的字符序列X=”x0,x1,x2,…xm-1”,序列Y=”y0,y1,…yk-1”是X的子序列,存在X的一个严格递增下标序列i=i0,i1,i2,…ik-1,使得对所有的j=0,1,2,…k-1,有xi=yi。例如X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。 给定两个序列A和B,称序列Z是A和B公共子序列,是指Z同是A和B的子序列。求最长公共子序列。 若A的长度为m,B的长度为n,则A的子序列有2*m-1个,B的子序列有2*n-1个。采用枚举法分别对A和B的所以子序列一一检查,最终求出最长公共子序列。如此比较次数(2*2n)接近指数阶,当n较大时,算法太耗时,不可取。所以要全面考虑不同的情况分别进行决策,,最后通过多阶段决策逐步找出问题的最终解.当各个阶段采取决策后,会不断决策出新的数据,直到找到最优解。 二、算法设计(或算法步骤) A=”a0,a1,a2,……am-1”,B=”b0,b1,b2,……bn-1”,且Z=”z0,z1,z2……zk-1”,为她们的最长公共子序列。不难证明有一下结论: (1)如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,z2,……zk-2”是“a0,a1,a2,…… am-2”和“b0,b1,b2,……bn-2”的一个最长公共子序列; (2)如果am-1!=bn-1,则若zk-1!=am-1,则“z0,z1,z2,……zk-1”是“a0,a1,a2,…… am-2”和”b0,b1,b2,……bn-1”的一个最长公共子序列。 (3)如果am-1!=bn-1,则若zk-1!=bn-1,则“z0,z1,z2,……zk-1”是“a0,a1,a2,…… am-1”和“b0,b1,b2,……bn-2”的一个最长公共子序列。 如此找到了原问题与其子问题的递归关系。 基本存储结构是存储两个字符串及其最长公共子序列的3个一位数组。当然要找出最长公共子序列,要存储当前最长公共子序列的长度和当前公共子序列的长度,而若只存储当前信息,最后只能求解最长公共子序列的长度,却不能找到最长公共子序列本身。因此需建立一个(n+1)*(m+1)的二维数组c,c[i][j]存储序列“a0,a1,a2……ai-2”和“b0,b1,……bj-1”的最长公共子序列长度,由上递推关系分析,计算c[i][j]可递归的表述如下: (1)c[i][j]=0 如果i=0或j=0;

最长公共子序列问题

2.3最长公共子序列问题 和前面讲的有所区别,这个问题的不涉及走向。很经典的动态规划问题。 例题16 最长公共子序列 (lcs.pas/c/cpp) 【问题描述】 一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X= < x1, x2,…, xm>,则另一序列Z= < z1, z2,…, zk>是X的子序列是指存在一个严格递增的下标序列< i1, i2,…, ik>,使得对于所有j=1,2,…,k有Xij=Zj 例如,序列Z=是序列X=的子序列,相应的递增下标序列为<2,3,5,7>。给定两个序列X 和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。例如,若X= < A, B, C, B, D, A, B>和Y= < B, D, C, A, B, A>,则序列是X和Y的一个公共子序列,序列也是X和Y的一个公共子序列。而且,后者是X和Y的一个最长公共子序列,因为X和Y没有长度大于4的公共子序列。 给定两个序列X= < x1, x2, …, xm>和Y= < y1, y2, … , yn>,要求找出X和Y的一个最长公共子序列。 【输入文件】 输入文件共有两行,每行为一个由大写字母构成的长度不超过200的字符串,表示序列X和Y。 【输出文件】 输出文件第一行为一个非负整数,表示所求得的最长公共子序列的长度,若不存在公共子序列,则输出文件仅有一行输出一个整数0,否则在输出文件的第二行输出所求得的最长公共子序列(也用一个大写字母组成的字符串表示。 【输入样例】 ABCBDAB BDCBA 【输出样例】 4 BCBA 【问题分析】 这个问题也是相当经典的。。 这个题目的阶段很不明显,所以初看这个题目没什么头绪,不像前面讲的有很明显的上一步,上一层之类的东西,只是两个字符串而且互相没什么关联。 但仔细分析发现还是有入手点的: 既然说是动态规划,那我们首先要考虑的就是怎么划分子问题,一般对于前面讲到的街道问题和数塔问题涉及走向的,考虑子问题时当然是想上一步是什么?但这个问题没有涉及走向,也没有所谓的上一步,该怎么办呢? 既然是求公共子序列,也就有第一个序列的第i个字符和第二个序列的第j个字符相等的情况。 那么我们枚第一个序列(X)的字符,和第二个序列(Y)的字符。 显然如果X[i]=Y[j]那么起点是1(下面说的子序列都是起点为1的),长度为i的子序列和长度为j的子序列的最长公共子序列就是长度为i-1和长度为j-1 的子序列中最长的公共子

最长公共子序列问题

实验三最长公共子序列问题 1.实验环境 本实验采用 java 语言编写实现,环境:,编译器: eclipse 2.实验目的 通过最长公共子序列问题,巩固并详细分析动态规划思想和解题 步骤。 3.设计思路 最长公共子序列的定义为:设有两个序列S[1..m]和9[仁n],需要寻找它们之间的一个最长公共子序列。 例如,假定有两个序列: S1: I N T H E B E G I N N I N G S2: A L L T H I N G S A R E L O S T 则S i和S的一个最长公共子序列为 THING又比如: S1: A B C B D A B S2: B D C A B A 则它们的一个最长公共子序列为 BCBA。 这里需要注意的是,一个子序列不一定必须是连续的,即中间可被其他字符分开,单它们的顺序必须是正确的。另外,最长公共子序列不一定只有一个,而我们需要寻找的是其中一个。

当然,如果要求子序列里面的元素必须连成一片也是可以的。实际上,连成一片的版本比这里实现的更容易。 4.过程 我们可以通过蛮力策略解决这个问题,步骤如下: 1.检查S1[1..m]里面每一个子序列。 2.看看其是否也是S2[1..n]里的子序列。 3.在每一步记录当前找到的子序列里面最长的子序列。 这种方法的效率十分低下。因此本实验采用动态规划的方法实现该算法。 利用动态规划寻找最长公共子序列步骤如下: 1.寻找最长公共子序列的长度。 2.扩展寻找长度的算法来获取最长公共子序列。 策略:考虑序列S1和S2的前缀序列。 设 c[i,j] = |LCS (S1[1..i],S2[1..j]),则有 c[m, n] = |LCS(S1 S2)| 所以有 c[ i -1 , j -1 ] + 1, 如要 S1[i] = S2[j] c[i, j]= max{ c [ i - 1, j ], c[ i , j -1 ] }, 如果 S1[i]工S2[j] 然后回溯输出最长公共子序列过程:

最长公共子序列实验报告

最长公共子序列问题 一.实验目的: 1.加深对最长公共子序列问题算法的理解,实现最长公共子序列问题的求解算法; 2.通过本次试验掌握将算法转换为上机操作; 3.加深对动态规划思想的理解,并利用其解决生活中的问题。 二.实验内容: 1.编写算法:实现两个字符串的最长公共子序列的求解; 2.将输入与输出数据保存在文件之中,包括运行时间和运行结果; 3.对实验结果进行分析。 三.实验操作: 1.最长公共子序列求解: 将两个字符串放到两个字符型数组中,characterString1和characterString2,当characterString1[m]= characterString2[m]时,找出这两个字符串m之前的最长公共子序列,然后在其尾部加上characterString1[m],即可得到最长公共子序列。当characterString1[m] ≠characterString2[m]时,需要解决两个子问题:即找出characterString1(m-1)和characterString2的一个最长公共子序列及characterString1和characterString2(m-1)的一个最长公共子序列,这两个公共子序列中较长者即为characterString1和characterString2的一个最长公共子序列。 2.动态规划算法的思想求解: 动态规划算法是自底向上的计算最优值。 计算最长公共子序列长度的动态规划算法LCS-Length以characterString1和characterString2作为输入,输出两个数组result和judge1,其中result存储最长公共子序列的长度,judge1记录指示result的值是由那个子问题解答得到的,最后将最终的最长公共子序列的长度记录到result中。 以LCS-Length计算得到的数组judge1可用于快速构造序列最长公共子序列。首先从judge1的最后开始,对judge1进行配对。当遇到“↖”时,表示最长公共子序列是由characterString1(i-1)和characterString2(j-1)的最长公共子序列在尾部加上characterString1(i)得到的子序列;当遇到“↑”时,表示最长公共子序列和characterString1(i-1)与characterString2(j)的最长公共子序列相同;当遇到“←”时,表示最长公共子序列和characterString1(i)与characterString2(j-1)的最长公共子序列相同。 如图所示:

最长公共子序列问题LCS-Read

最长公共子序列问题LCS 问题描述 一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X=,则另一序列Z=是X的子序列是指存在一个严格递增的下标序列,使得对于所有j=1,2,…,k有 例如,序列Z=是序列X=的子序列,相应的递增下标序列为<2,3,5,7>。 给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。例如,若X=和Y=,则序列是X和Y的一个公共子序列,序列也是X和Y的一个公共子序列。而且,后者是X和Y的一个最长公共子序列,因为X和Y没有长度大于4的公共子序列。 最长公共子序列(LCS)问题:给定两个序列X=和Y=,要求找出X和Y的一个最长公共子序列。 参考解答 动态规划算法可有效地解此问题。下面我们按照动态规划算法设计的各个步骤来设计一个解此问题的有效算法。 1.最长公共子序列的结构 解最长公共子序列问题时最容易想到的算法是穷举搜索法,即对X的每一个子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列,并且在检查过程中选出最长的公共子序列。X的所有子序列都检查过后即可求出X和Y的最长公共子序列。X 的一个子序列相应于下标序列{1, 2, …, m}的一个子序列,因此,X共有2m个不同子序列,从而穷举搜索法需要指数时间。 事实上,最长公共子序列问题也有最优子结构性质,因为我们有如下定理: 定理: LCS的最优子结构性质 设序列X=和Y=的一个最长公共子序列Z=,则: 1.若x m=y n,则z k=x m=y n且Z k-1是X m-1和Y n-1的最长公共子序列; 2.若x m≠y n且z k≠x m ,则Z是X m-1和Y的最长公共子序列;

最长公共子序列问题

实验三最长公共子序列问题 1. 实验环境 本实验采用java 语言编写实现,环境:,编译器:eclipse 2. 实验目的 通过最长公共子序列问题,巩固并详细分析动态规划思想和解题步骤。 最长公共子序列的定义为:设有两个序列S i[1..m]和84仁n],需要寻找它们之间的一个最长公共子序列。 例如,假定有两个序列: 81:I N T H E B E G I N N I N G 82:A L L T H I N G 8 A R E L O 8 T 则8i和S的一个最长公共子序列为THING又比如: 81:A B C B D A B 82:B D C A B A 则它们的一个最长公共子序列为BCBA。 这里需要注意的是,一个子序列不一定必须是连续的,即中间可被其他字符分开,单它们的顺序必须是正确的。另外,最长公共子序列不一定只有一个,而我们需要寻找的是其中一个。

当然,如果要求子序列里面的元素必须连成一片也是可以的。实 际上,连成一片的版本比这里实现的更容易。 4.过程 我们可以通过蛮力策略解决这个问题,步骤如下: 1. 检查S1[1..m]里面每一个子序列。 2. 看看其是否也是S2[1..n]里的子序列。 3. 在每一步记录当前找到的子序列里面最长的子序列。 这种方法的效率十分低下。因此本实验采用动态规划的方法实现 该算法。 利用动态规划寻找最长公共子序列步骤如下: 1.寻找最长公共子序列的长度。 2.扩展寻找长度的算法来获取最长公共子序列。 策略:考虑序列S1和S2的前缀序列。 设 c[i ,j] = |LCS (S1[1..i],S2[1..j]),则有 c[m , n] = |LCS(S1 S2)| 所以有 c[i ,j]= max{ c [ i - 1, j ], c[ i , j -1 ] }, c[ i -1 , j -1 ] + 1, 如要 S1[i] = S2[j] 如果 S1[i]工 S2[j]

动态规划法求解最长公共子序列(含Java代码)

公共子序列问题徐康123183 一.算法设计 假设有两个序列X和Y,假设X和Y分别有m和n个元素,则建立一个二维数组C[(m+1)*(n+1)],记录X i与Y j的LCS的长度。将C[i,j]分为三种情况: 若i =0 或j =0时,C[i,j]=0; 若i,j>0且X[i]=Y[j],C[i,j]=C[i-1,j-1]+1; 若i,j>0且X[i] Y[j],C[i,j]=max{C[i-1,j],C[i,j-1]}。 再使用一个m*n的二维数组b,b[i,j]记录C[i,j]的来向: 若X[i]=Y[j],则B[i,j]中记入“↖”,记此时b[i,j] = 1; 若X[i] Y[j]且C[i-1,j] > C[i,j-1],则b[i,j]中记入“↑”,记此时B[i,j] = 2; 若X[i] Y[j]且C[i-1,j] < C[i,j-1],则b[i,j]中记入“←”,记此时B[i,j] = 3; 若X[i]Y[j]且C[i-1,j] = C[i,j-1],则b[i,j]中记入“↑”或“←”,记此时B[i,j] = 4; 得到了两个数组C[]和B[],设计递归输出LCS(X,Y)的算法: LCS_Output(Direction[][], X[], i, j, len,LCS[]){ If i=0 or j=0 将LCS[]保存至集合LCS_SET中 then return; If b[i,j]=1 then /*X[i]=Y[j]*/ {LCS_Output(b,X,i-1,j-1); 将X[i]保存至LCS[len-i];} else if b[i,j]=2 then /*X[i]Y[j]且C[i-1,j]>C[i,j-1]*/ LCS_Output(b,X,i-1,j) else if b[i,j]=3 then /*X[i]Y[j]且C[i-1,j]

最长公共子序列

动态规划

一、问题描述 用动态规划法求两个字符串A=‘xzyzzyx’和B=‘zxyyzxz’的最长公共子序列 二、算法分析 (1)、若xm=yn,则zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共自序列; (2)、若xm≠yn,且zk≠xm,则Zk是Xm-1和Yn的最长公共自序列; (3)、若xm≠yn,且zk≠yn,则Zk是Xm和Yn-1的最长公共自序列;设L(m,n)表示序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列的长度 L表示已经决策的长度 S表示每个决策的状态 L(0,0)=L(0,j)=0 1≤i≤m, 1≤j≤n L(i-1,j-1)+1 xi=yi,i≥1,j≥1 L(i,j)= max{L(i,j-1),(L(i-1,j)} xi≠yi,i≥1,j≥1 1 xi=yi S(i,j)= 2 xi≠yi 且L(i,j-1)≥L(i-1,j) 3 xi≠yi 且L(i,j-1)< L(i-1,j)

长度矩阵L 三、源代码 #include #include using namespace std; int main() { string str1 = "xzyzzyx"; string str2 = "zxyyzxz"; int x_len = str1.length(); int y_len = str2.length();

int arr[50][50] ={{0,0}}; int i = 0; int j = 0; for(i = 1; i <= x_len; i++) { for(j = 1; j <= y_len; j++) { if(str1[i - 1] == str2[j - 1]) { arr[i][j] = arr[i - 1][j - 1] + 1; } else if(arr[i][j - 1] >= arr[i - 1][j]) arr[i][j] = arr[i][j - 1]; else arr[i][j] = arr[i -1][j]; } } for(i = 0 ; i <= x_len; i++) {

最长公共子序列LCS 问题的matlab实现代码

(以下代码可直接带入matlab运行,首先以.m文件保存第一段代码,然后在command window输入第二段代码即可) function D=substringArray(A,B) na=length(A); nb=length(B); C=zeros(nb,na); for i=1:nb C(i,1)=0; end for j=1:na C(1,j)=0; end for i=2:nb for j=2:na if B(i-1)==A(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); end end end end valmax=C(nb,na); i=nb; j=na; D=''; while i>1 & j>1 if C(i,j)==C(i-1,j-1)+1 & A(j-1)==B(i-1) D=strcat(B(i-1),D) ; i=i-1; j=j-1; else if C(i,j)==C(i,j-1) j=j-1; else i=i-1; end end end

%A='ACGTAACCT'; %B='GGACTTAGG'; %A='abcbs'; %B='bcabf'; A=getgenbank('NC_002017','SEQUENCEONLY',true); B=getgenbank('NC_002018','SEQUENCEONLY',true); substring=substringArray(A,B)

实验三:最长公共子序列

实验三:最长公共子序列 实验目的:掌握使用动态规划策略编程实现最长公共子序列; 实验原理:动态规划算法设计。 实验要求:基本掌握动态规划算法的原理方法。熟练掌握VC++中编程实现算法的常用技术和方法。 问题描述:给定两个序列X = { x1 , x2 , ... , xm }Y = { y1 , y2 , ... , yn }求X和Y的一个最长公共子序列 思路:最长公共子序列问题具有最优子结构性质 设X = { x1 , ... , xm }Y = { y1 , ... , yn }及它们的最长子序列Z = { z1 , ... , zk } 则 1、若 xm = yn ,则 zk = xm = yn,且Z[k-1] 是 X[m-1] 和 Y[n-1] 的最长公共子序列 2、若 xm != yn ,且 zk != xm , 则 Z 是 X[m-1] 和 Y 的最长公共子序列 3、若 xm != yn , 且 zk != yn , 则 Z 是 Y[n-1] 和 X 的最长公共子序列 由性质导出子问题的递归结构 当 i = 0 , j = 0 时 , c[i][j] = 0 当 i , j > 0 ; xi = yi 时 , c[i][j] = c[i-1][j-1] + 1 当 i , j > 0 ; xi != yi 时 , c[i][j] = max { c[i][j-1] , c[i-1][j] } #include "iostream.h" #include "iomanip.h" #define max 100 void LCSLength( int m , int n , char *x , char *y , char *b ) { int i , j , k; int c[max][max]; for( i = 1 ; i <= m ; i++ ){ c[i][0] = 0;} for( i = 1 ; i <= n ; i++ ){ c[0][i] = 0;} for( i = 1 ; i <= m ; i++ ) { for( j = 1 ; j <= n ; j++ ) { if( x[i-1] == y[j-1] ){ c[i][j] = c[i-1][j-1] + 1; k = i * ( n + 1 ) + j; b[k] = '\\';} else if( c[i-1][j] >= c[i][j-1] ){ c[i][j] = c[i-1][j]; k = i * ( n + 1 ) + j; b[k] = '|';} else{ c[i][j] = c[i][j-1];

最长公共子序列问题

1. 实验环境 本实验采用java语言编写实现,环境:,编译器:eclipse 2. 实验目的 通过最长公共子序列问题,巩固并详细分析动态规划思想和解题步骤。 3. 设计思路 最长公共子序列的定义为:设有两个序列S1[1..m]和S2[1..n],需要寻找它们之间的一个最长公共子序列。 例如,假定有两个序列: S1:I N T H E B E G I N N I N G S2:A L L T H I N G S A R E L O S T 则S1和S2的一个最长公共子序列为THING。又比如: S1:A B C B D A B S2:B D C A B A 则它们的一个最长公共子序列为BCBA。 这里需要注意的是,一个子序列不一定必须是连续的,即中间可被其他字符分开,单它们的顺序必须是正确的。另外,最长公共子序列不一定只有一个,而我们需要寻找的是其中一个。 当然,如果要求子序列里面的元素必须连成一片也是可以的。实际上,连成一片的版本比这里实现的更容易。

4. 过程 我们可以通过蛮力策略解决这个问题,步骤如下: 1. 检查S1[1..m]里面每一个子序列。 2. 看看其是否也是S2[1..n]里的子序列。 3. 在每一步记录当前找到的子序列里面最长的子序列。 这种方法的效率十分低下。因此本实验采用动态规划的方法实现该算法。 利用动态规划寻找最长公共子序列步骤如下: 1. 寻找最长公共子序列的长度。 2. 扩展寻找长度的算法来获取最长公共子序列。 策略:考虑序列S1和S2的前缀序列。 设c[i,j] = |LCS (S1[1..i],S2[1..j])|,则有c[m,n] = |LCS(S1,S2)| 所以有 c[ i – 1 , j – 1 ] + 1,如要S1[i] = S2[j] c[i,j] = max{ c [ i - 1,j ],c[ i ,j -1 ] },如果S1[i]≠S2[j] 然后回溯输出最长公共子序列过程:

用动态规划法解决最长公共子序列问题

动态规划解最长子序列 一、课程设计目的 掌握动态规划法的原理,并能够按其原理编程实现求两个序列数据的最长公共子系列,以加深对其的理解。 二、课程设计内容 1、用动态规划法解决最长子序列问题 2、交互输入两个序列数据 3、输出两个序列的最长公共子序列 三、概要设计 四、详细设计与实现 #include "iostream.h" #include "iomanip.h" #define max 100 void LCSLength(int m,int n,char *x,char *y,char *b) { int i,j,k; int c[max][max]; for(i=1;i<=m;i++) { c[i][0]=0; } for(i=1;i<=n;i++) { c[0][i]=0; } for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { if(x[i-1]==y[j-1]) { c[i][j]=c[i-1][j-1]+1; k=i*(n+1)+j; b[k]='\\'; } else if(c[i-1][j]>=c[i][j-1]) {

c[i][j]=c[i-1][j]; k=i*(n+1)+j; b[k]='|'; } else{ c[i][j]=c[i][j-1]; k=i*(n+1)+j; b[k]='-'; } } } } void LCS(int i,int j,char *x,char *b,int width) { if(i==0 || j==0) return; int k=i*(width+1)+j; if(b[k]=='\\'){ LCS(i-1,j-1,x,b,width); cout<

算法设计-最长公共子序列

最长公共子序列(LCS)算法 一.算法要求及分析 1. 算法要求:利用b[i,j],设计一个算法求出全部的LCS;利用”会计方法”, 分析所编算法的时间复杂度的最坏情况。 2. 算法分析:该部分思路同课件 二.算法详细设计 为了求出全部的LCS,需要设计两个功能函数:LCS_L和LCS_Output,函数LCS_L实现计算LCS长度及每个子问题的由来;函数LCS_Output用递归方法实现输出所有LCS。 具体设计实现思路: 1. 声明全局变量二维动态数组C和b。数组C记录所要求的LCS的长度;数组b记录C[i,j]是通过哪一个子问题的值求得的。定义枚举类型记录不同的遍历方向。 2. 用动态规划法实现功能函数LCS_L,得出数组C和b。 函数实现思路:首先动态分配和初始化二维数组C和b,然后计算出C和b。 根据X[i]和Y[j]的关系,计算得出C[i,j]: ①若X[i]=Y[j],则执行C[i,j]←C[i-1,j-1]+1且b[i][j]=ual; ②若X[i]!=Y[j],则分为三种情况: 若C[i-1,j]>C[i,j-1]则C[i,j]取C[i-1,j]且b[i][j]=up; 若C[i-1,j]

实验二 最长公共子序列问题

实验二最长公共子序列问题 一、实验目的: 1、理解动态规划算法的概念; 2、掌握动态规划算法的基本要素; 3、掌握设计动态规划算法的步骤; 4、通过应用范例学习动态规划算法的设计技巧与策略; 二、实验内容及要求: 1、使用动态规划算法解决最长公共子序列问题:给定两个序列 X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。。 2、通过上机实验进行算法实现。 3、保存和打印出程序的运行结果,并结合程序进行分析,上交实验报告。 三、实验原理: 动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家 R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。1957年出版了他的名著Dynamic Programming,这是该领域的第一本著作。 算法总体思想: 1)动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。 2)与分治法不同的是,适合于用动态规划法求解的问题,经分解得到的子问题往往不是独立的。子问题中存在大量的公共子问题,在分治求解过程中被多次重复计算,保存计算结果,为后面的计算直接引用,减少重复计算次数这就是动态规划的基本思想。 3)用动态规划算法求解问题,可依据其递归式以自底向上的方式进行计算。在计算过程中,保存已解决的子问题的答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量重复计算,最终得到多项式时间算法。 动态规划基本步骤:

相关主题