最长公共子序列问题
- 格式:doc
- 大小:26.00 KB
- 文档页数:4
最长公共子序列(LCS)问题(非连续子序列)的两种解法最长公共子序列也称作最长公共子串,英文缩写是LCS(Longest Common Subsequence)。
其定义是:一个序列S,如果分别是两个或多个已知序列的子序列,且是符合此条件的子序列中最长的,则称S为已知序列的最长公共子序列。
关于子序列的定义通常有两种方式,一种是对子序列没有连续的要求,其子序列的定义就是原序列中删除若干元素后得到的序列。
另一种是对子序列有连续的要求,其子序列的定义是原序列中连续出现的若干个元素组成的序列。
求解子序列是非连续的最长公共子序列问题是一个十分实用的问题,它可以描述两段文字之间的“相似度”,即它们的雷同程度,从而能够用来辨别抄袭。
本文将介绍对子序列没有连续性要求的情况下如何用计算机解决最长公共子序列问题,对子序列有连续性要求的情况下如何用计算机解决最长公共子序列问题将在后续的文章中介绍。
一、动态规划法(Dynamic Programming)最长公共子序列问题应该是属于多阶段决策问题中求最优解一类的问题,凡此类问题在编制计算机程序时应优先考虑动态规划法,如果不能用动态规划法,而且也找不到其它解决方法,还可以考虑穷举法。
对于这个问题,只要能找到描述最长公共子序列的最优子结构和最优解的堆叠方式,并且保证最优子结构中的每一次最优决策都满足“无后效性”,就可以考虑用动态规划法。
使用动态规划法的关键是对问题进行分解,按照一定的规律分解成子问题(分解后的子问题还可以再分解,这是个递归的过程),通过对子问题的定义找出最优子结构中最优决策序列(对于子问题就是最有决策序列的子序列)以及最优决策序列子序列的递推关系(当然还包括递推关系的边界值)。
如果一个给定序列的子序列是在该序列中删去若干元素后得到的序列,也就意味着子序列在原序列中的位置索引(下标)保持严格递增的顺序。
例如,序列S = <B,C,D,B>是序列K = <A,B,C,B,D,A,B>的一个子序列(非连续),序列S的元素在在K中的位置索引I = [2,3,5,7],I是一个严格递增序列。
最长公共子序列问题;最长公共子序列(Longest Common Subsequence, LCS)问题是指找到多个字符串中最长的公共子序列。
公共子序列是指这些字符串在所有字符串中都以相同的顺序出现,但不要求连续。
例如,对于字符串"ABCD"和"ACDF",其最长公共子序列为"ACD"。
最长公共子序列问题可以通过动态规划来解决。
基本思路是使用一个二维数组dp,其中dp[i][j]表示字符串A的前i个字符和字符串B的前j个字符的最长公共子序列的长度。
首先,初始化dp[0][j]和dp[i][0]为0,表示空字符串与任何字符串的最长公共子序列长度为0。
然后,对于每个字符A[i]和B[j],如果A[i]等于B[j],则dp[i][j] = dp[i-1][j-1] + 1,表示在之前的最长公共子序列长度的基础上加1。
否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1]),表示要么在字符串A的前i-1个字符和字符串B的前j个字符的最长公共子序列的基础上取得,要么在字符串A的前i个字符和字符串B的前j-1个字符的最长公共子序列的基础上取得。
最终,dp[m][n]即为所求,其中m和n分别为字符串A和字符串B的长度。
可以通过追踪dp数组来还原出最长公共子序列,具体方法是从dp[m][n]开始,如果A[i]等于B[j],则该字符是公共字符,将其加入结果序列,然后向左上方移动,即dp[i-1][j-1]。
如果dp[i][j]等于dp[i-1][j],则说明最长公共子序列在A中取得,向上移动,即dp[i-1][j];如果dp[i][j]等于dp[i][j-1],则说明最长公共子序列在B中取得,向左移动,即dp[i][j-1]。
重复上述过程,直到dp[i][j]为0。
动态规划经典——最长公共⼦序列问题(LCS)和最长公共⼦串问题⼀.最长公共⼦序列问题(LCS问题)给定两个字符串A和B,长度分别为m和n,要求找出它们最长的公共⼦序列,并返回其长度。
例如: A = "Hel lo W o rld" B = "loo p"则A与B的最长公共⼦序列为 "loo",返回的长度为3。
此处只给出动态规划的解法:定义⼦问题dp[i][j]为字符串A的第⼀个字符到第 i 个字符串和字符串B 的第⼀个字符到第 j 个字符的最长公共⼦序列,如A为“app”,B为“apple”,dp[2][3]表⽰ “ap” 和 “app” 的最长公共字串。
注意到代码中 dp 的⼤⼩为 (n + 1) x (m + 1) ,这多出来的⼀⾏和⼀列是第 0 ⾏和第 0 列,初始化为 0,表⽰空字符串和另⼀字符串的⼦串的最长公共⼦序列,例如dp[0][3]表⽰ "" 和“app” 的最长公共⼦串。
当我们要求dp[i][j],我们要先判断A的第i个元素B的第j个元素是否相同即判断A[i - 1]和 B[j -1]是否相同,如果相同它就是dp[i-1][j-1]+ 1,相当于在两个字符串都去掉⼀个字符时的最长公共⼦序列再加 1;否则最长公共⼦序列取dp[i][j - 1] 和dp[i - 1][j]中⼤者。
所以整个问题的初始状态为:dp[i][0]=0,dp[0][j]=0相应的状态转移⽅程为:dp[i][j]=max{dp[i−1][j],dp[i][j−1]},A[i−1]!=B[j−1] dp[i−1][j−1]+1,A[i−1]==B[j−1]代码的实现如下:class LCS{public:int findLCS(string A, int n, string B, int m){if(n == 0 || m == 0)//特殊输⼊return 0;int dp[n + 1][m + 1];//定义状态数组for(int i = 0 ; i <= n; i++)//初始状态dp[i][0] = 0;for(int i = 0; i <= m; i++)dp[0][i] = 0;for(int i = 1; i <= n; i++)for(int j = 1; j<= m; j++){if(A[i - 1] == B[j - 1])//判断A的第i个字符和B的第j个字符是否相同dp[i][j] = dp[i -1][j - 1] + 1;elsedp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);}return dp[n][m];//最终的返回结果就是dp[n][m]}};该算法的时间复杂度为O(n*m),空间复杂度为O(n*m)。
目录1 前言02 需求分析12.1 任务和要求12.2 运行环境12.3 开发工具13 分析和设计13.1 系统分析及设计思路13.2 主要数据结构及算法23.3 函数流程图24 具体代码实现35 课程设计总结135.1 程序运行结果或预期运行结果135.2 设计结论13参考文献14致谢141 前言若给定序列X={x1,x2,…,x m},则另一序列Z={z1,z2,…,z k},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。
例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。
给定2个序列X和Y,当另一序列Z既是X的子序列又是Y 的子序列时,称Z是序列X和Y的公共子序列。
请使用C语言编程,设计一个有效的算法解决下述问题:给定2个序列X={x1,x2,…,x m}和Y={y1,y2,…,y n},找出X和Y 的最长公共子序列。
2 需求分析2.1任务和要求使用C语言编程,设计一个有效的算法解决下述问题:给定2个序列X={x1,x2,…,x m}和Y={y1,y2,…,y n},找出X和Y的最长公共子序列。
2.2运行环境(1)WINDOWS2000/XP系统(2)Visual C++ 6.0编译环境或TC编译环境2.3开发工具C语言3 分析和设计3.1 系统分析及设计思路设序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列为Z={z1,z2,…,zk} ,则(1)若xm =yn,则zk=xm=yn,且zk-1是xm-1和yn-1的最长公共子序列。
(2)若xm ≠yn且zk≠xm,则Z是xm-1和Y的最长公共子序列。
(3)若xm ≠yn且zk≠yn,则Z是X和yn-1的最长公共子序列。
由此可见,2个序列的最长公共子序列包含了这2个序列的前缀的最长公共子序列。
动态规划解最长公共子序列问题动态规划主要针对最优化问题,它的决策是全面考虑不同的情况分别进行决策,,最后通过多阶段决策逐步找出问题的最终解.当各个阶段采取决策后,会不断决策出新的数据,直到找到最优解.每次决策依赖于当前状态,又随机引起状态的转移.一个决策序列就是在变化的状态中产生出来的,故有”动态”的含义.所以,这种多阶段最优化决策解决问题的过程称为动态规划.一问题的描述与分析字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干字符(可能一个也不去掉)后形成的字符序列..令给定的字符序列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”的一个最长公共子序列。
最长公共子序列问题(LCS)(生物信息学中常用算法)子序列的概念:设X=< x1, x2,┅, x m>,若有1≤i1< i2< ┅<i k≤m,使得Z=< z1, z2,┅, z k> = < x i1, x i2,┅, x ik>,则称Z是X的子序列,记为Z<X。
e.g. X=<A,B,C,B,D,A,B>, Z=<B,C,B,A>, 则有Z<X。
公共子序列的概念:设X,Y是两个序列,且有Z<X和Z<Y,则称Z是X和Y 的公共子序列。
最长公共子序列的概念:若Z<X,Z<Y,且不存在比Z更长的X和Y 的公共子序列,则称Z是X和Y 的最长公共子序列,记为Z∈LCS(X , Y)。
最长公共子序列往往不止一个。
e.g. X=<A,B,C,B,D,A,B>, Y=<B,D,C,A,B,A>, 则Z=<B,C,B,A>, Z’=<B,C,A,B>, Z’’=<B,D,A,B>均属于LCS(X , Y) ,即X,Y有3个LCS。
如何找出X和Y的一个最长公共子序列?Brute-force法:列出X的所有长度不超过n(即∣Y∣)的子序列,从长到短逐一进行检查,看其是否为Y的子序列,直到找到第一个最长公共子序列。
由于X共有2m个子序列,故此方法对较大的m没有实用价值。
是否能使用动态规划法?如何用?分析:记X i=﹤x1,…,x i﹥即X序列的前i个字符(1≤i≤m)(前缀)Y j=﹤y1,…,y j﹥即Y序列的前j个字符(1≤j≤n)(前缀)假定Z=﹤z1,…,z k﹥∈LCS(X , Y)。
若x m=y n(最后一个字符相同),则不难用反证法证明:该字符必是X与Y的任一最长公共子序列Z(设长度为k)的最后一个字符,即有z k = x m = y n且显然有Z k-1∈LCS(X m-1 , Y n-1) 即Z的前缀Z k-1是X m-1与Y n-1的最长公共子序列。
算法作业: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 jy i 且x ,i,j ]][j C[i jy 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] = 2, 若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)问题有两种方式定义子序列,一种是子序列不要求不连续,一种是子序列必须连续。
上一章介绍了用两种算法解决子序列不要求连续的最终公共子序列问题,本章将介绍要求子序列必须是连续的情况下如何用算法解决最长公共子序列问题。
仍以上一章的两个字符串“abcdea”和“aebcda”为例,如果子序列不要求连续,其最长公共子序列为“abcda”,如果子序列要求是连续,则其最长公共子序列应为“bcd”。
在这种情况下,有可能两个字符串出现多个长度相同的公共子串,比如“askdfiryetd”和“trkdffirey”两个字符串就存在两个长度为3的公共子串,分别是“kdf”和“fir”,因此问题的性质发生了变化,需要找出两个字符串所有可能存在公共子串的情况,然后取最长的一个,如果有多个最长的公共子串,只取其中一个即可。
字符串“abcdea”和“aebcda”如果都以最左端的a字符对齐,则能够匹配的最长公共子串就是“a”。
但是如果用第二个字符串的e字符对齐第一个字符串的a 字符,则能够匹配的最长公共子串就是“bcd”。
可见,从两个字符串的不同位置开始对齐匹配,可以得到不同的结果,因此,本文采用的算法就是穷举两个字符串所有可能的对齐方式,对每种对齐方式进行字符的逐个匹配,找出最长的匹配子串。
一、递归方法首先看看递归方法。
递归的方法比较简单,就是比较两个字符串的首字符是否相等,如果相等则将其添加到已知的公共子串结尾,然后对两个字符串去掉首字符后剩下的子串继续递归匹配。
如果两个字符串的首字符不相等,则用三种对齐策略分别计算可能的最长公共子串,然后取最长的一个与当前已知的最长公共子串比较,如果比当前已知的最长公共子串长就用计算出的最长公共子串代替当前已知的最长公共子串。
第一种策略是将第一个字符串的首字符删除,将剩下的子串与第二个字符串继续匹配;第二种策略是将第二个字符串的首字符删除,将剩下的子串与第一个字符串继续匹配;第三种策略是将两个字符串的首字符都删除,然后继续匹配两个字符串剩下的子串。
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。
最长公共子序列问题是给定两个序列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] }由以上分析,先计算最优值,再构造最长公共子序列,C++源程序如下:#include <stdio.h>#include <string.h>#include"iostream"using namespace std;int length=0; //记录最长公共序列长度//计算最优值void lcs(string x,string y,int **b){int i,j;//获取字符串的长度int m = x.size();int n = y.size();//创建动太二维数组int **c;c = new int*[m+1];for(int i = 0;i < m+1;i++)c[i] = new int[n+1];//初始化数组for (i=1;i<=m;i++) c[i][0] = 0;for (i=1;i<=n;i++) c[0][i] = 0;c[0][0] = 0;//遍历记录最优值,并将标识存在数组b中for (i=1;i<=m;i++)for (j=1;j<=n;j++){if (x[i-1] == y[j-1]){ //xm = yn ,则 zk = xm = yn,且Z[k-1] 是 X[m-1] 和 Y[n-1] 的最长公共子序列c[i][j] = c[i-1][j-1] + 1;b[i][j] = 1;}else if (c[i-1][j] > c[i][j-1]){ //xm != yn ,且 zk != xm , 则 Z 是 X[m-1] 和 Y 的最长公共子序列c[i][j] = c[i-1][j];b[i][j] = 2;}else{ //xm != yn , 且 zk != yn , 则 Z 是 Y[n-1] 和 X 的最长公共子序列c[i][j] = c[i][j-1];b[i][j] = 3;}}//释放空间for(int i = 0;i < m+1;i++)delete c[i];delete c;}//构造最长公共子序列void show(int i,int j,string x,int **b){//递归中止条件if (i==0||j==0)return ;//从b[m][n]开始,用递归依其值在数组b中搜索if (b[i][j]==1){ //xi和yj的最长公共子序列是由x(i-1)和y(j-1)的最长公共子序列在尾部加上xi所得到的子序列show(i-1,j-1,x,b);length++;cout << x[i-1];}else if (b[i][j]==2){ //xi和yi的最长公共子序列与x(i-1)和yj的最长公共子序列相同show(i-1,j,x,b);}else{ //xi和yi的最长公共子序列与xi和y(j-1)的最长公共子序列相同show(i,j-1,x,b);}}int main(){string x;string y;int **b;cout << "输入第一个序列";cin >> x;cout << "输入第二个序列";cin >> y;int m = x.size();int n = y.size();//创建二维动态数组b = new int*[m+1];for(int i = 0;i < m+1;i++)b[i] = new int[n+1];//求解最优值lcs(x,y,b);//构造序列并打印cout << "最长公共序列:";show(m,n,x,b);cout << "\n长度:" << length << endl;//释放空间for(int i = 0;i < m+1;i++)delete b[i];delete b;system("pause");return 0;}。
动态规划解最长公共子序列问题2009-05-30 21:28 36702人阅读评论(36) 收藏举报c算法动态规划法经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题。
简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加。
为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法。
【问题】求两字符序列的最长公共字符子序列问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。
令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X1的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。
例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。
不难证明有以下性质:(1)如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;(2)如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;(3)如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。
2这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
最长公共子序列问题 kmp算法下载提示:该文档是本店铺精心编制而成的,希望大家下载后,能够帮助大家解决实际问题。
文档下载后可定制修改,请根据实际需要进行调整和使用,谢谢!本店铺为大家提供各种类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by this editor. I hope that after you download it, it can help you solve practical problems. The document can be customized and modified after downloading, please adjust and use it according to actual needs, thank you! In addition, this shop provides you with various types of practical materials, such as educational essays, diary appreciation, sentence excerpts, ancient poems, classic articles, topic composition, work summary, word parsing, copy excerpts, other materials and so on, want to know different data formats and writing methods, please pay attention!最长公共子序列问题(Longest Common Subsequence Problem)是指在两个序列中找到一个最长的子序列,使得这个子序列在两个原序列中按照顺序出现,但不一定是连续的。
算法导论最长公共子序列
最长公共子序列的解法有两种,一种是暴力搜索,一种是动态规划。
暴力搜索算法则是检查所有可能的子序列,然后找出最长的公共子序列。
动态规划算法通过使用子问题的重叠,来解决这个问题。
动态规划的思想是,使用一个二维数组来存储子问题的解。
这个二维数组的每个单元都代表着两个字符串之间相应位置上字符的最长公共子序列的长度。
我们可以用如下公式来求解:
LCS(i,j)=。
1. LCS(i-1, j-1) + 1, if X[i] = Y[j] 。
2. max( LCS(i, j-1), LCS(i-1, j) ), if X[i] != Y[j].
最终的答案是从二维数组的右下角的值。
最长公共子序列测试用例最长公共子序列(Longest Common Subsequence)是一种经典的问题,涉及到字符串处理和动态规划,具有广泛的应用场景。
在本文中,我们将详细探讨最长公共子序列问题,并给出一些有指导意义的测试用例。
首先,让我们先明确一下最长公共子序列的定义。
给定两个序列,我们需要找到它们最长的公共子序列,即能够从两个序列中分别取出若干元素,但相对顺序保持不变的最长子序列。
例如,对于序列"ABCD"和"ACDF",它们的最长公共子序列是"ACD"。
接下来,我们来考虑一些测试用例。
首先,我们可以选择两个完全相同的序列,这样最长公共子序列就是它们本身。
例如,对于序列"ABCD"和"ABCD",它们的最长公共子序列就是它们本身。
其次,我们可以选择两个没有公共子序列的序列。
例如,对于序列"ABCD"和"EFGH",它们之间没有任何相同的元素,因此最长公共子序列为空。
另外,我们还可以选择两个长度相同但内容不同的序列,这将涉及到如何处理不同元素的情况。
例如,对于序列"ABCD"和"DCBA",它们的最长公共子序列是"AD"。
我们需要验证算法是否能够正确处理元素的顺序。
此外,我们还需要考虑一个序列是另一个序列的子序列的情况。
例如,对于序列"ABC"和"ABCD",它们的最长公共子序列是"ABC",其中一个序列是另一个序列的子序列。
我们需要验证算法是否能够正确处理这种情况。
最后,我们可以选择两个长度不同但内容相同的序列,这将测试算法在处理不同长度序列时的表现。
例如,对于序列"ABCDE"和"BCD",它们的最长公共子序列是"BCD"。
的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。
例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。
不难证明有以下性质:(1)如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;(2)如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;(3)如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。
这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
求解:引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。
此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。
⽤Python计算最长公共⼦序列和最长公共⼦串1. 什么是最长公共⼦序列?什么是最长公共⼦串?1.1. 最长公共⼦序列(Longest-Common-Subsequences,LCS)最长公共⼦序列(Longest-Common-Subsequences,LCS)是⼀个在⼀个序列集合中(通常为两个序列)⽤来查找所有序列中最长⼦序列的问题。
这与查找最长公共⼦串的问题不同的地⽅是:⼦序列不需要在原序列中占⽤连续的位置。
最长公共⼦序列问题是⼀个经典的计算机科学问题,也是数据⽐较程序,⽐如Diff⼯具,和⽣物信息学应⽤的基础。
它也被⼴泛地应⽤在版本控制,⽐如Git⽤来调和⽂件之间的改变。
1.2 最长公共⼦串(Longest-Common-Substring,LCS)最长公共⼦串(Longest-Common-Substring,LCS)问题是寻找两个或多个已知字符串最长的⼦串。
此问题与最长公共⼦序列问题的区别在于⼦序列不必是连续的,⽽⼦串却必须是连续的。
2. 如何求解最长公共⼦序列?例如序列str_a=world,str_b=wordl。
序列wo是str_a和str_b的⼀个公共⼦序列,但是不是str_a和str_b的最长公共⼦序列,⼦序列word是str_a和str_b的⼀个LCS,序列worl也是。
暴⼒查找?寻找LCS的⼀种⽅法是枚举X所有的⼦序列,然后注意检查是否是Y的⼦序列,并随时记录发现的最长⼦序列。
假设X有m个元素,则X有2^m个⼦序列,指数级的时间,对长序列不实际。
2.1 基于递归的⽅法根据上边分析结果,可以写出简洁易懂的递归⽅法。
def recursive_lcs(str_a, str_b):if len(str_a) == 0 or len(str_b) == 0:return 0if str_a[0] == str_b[0]:return recursive_lcs(str_a[1:], str_b[1:]) + 1else:return max([recursive_lcs(str_a[1:], str_b), recursive_lcs(str_a, str_b[1:])])print(recursive_lcs('qweasde', 'asdzeexc'))2.2 基于⾃底向上动态规划的⽅法根据上述分析问题,动态规划递推公式也⾮常明显,可以写出动态规划代码:转载:https:///CheeseZH/p/8830482.html。
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 的子序列中最长的公共子
序列加上X[i]或Y[j]。
那要是不相等呢?
如果不相等,也就是说第一个序列长度为I的子序列和第二个序列中长度为j的子序列的公共子序列中X[i]和Y[j]不同时出现。
也就是说第一个序列长度为i的子序列和第二个序列中长度为j的子序列的公共子序列是第一个序列长度为i的子序列和第二个序列中长度为j-1的子序列或第一个序列长度为i-1的子序列和第二个序列中长度为j的子序列的公共子序列中一个更长的。
给定两个序列
X = { x1 , x2 , ... , xm }
Y = { y1 , y2 , ... , yn }
求X和Y的一个最长公共子序列
举例
X = { a , b , c , b , d , a , b }
Y = { b , d , c , a , b , a }
最长公共子序列为
LSC = { b , c , b , a }
分析:
最长公共子序列问题具有最优子结构性质
设
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>
#include <iomanip>
#include <fstream>
using namespace std;
#define max 100
ifstream fin("LCS.IN");
ofstream fout("LCS.OUT");
int total=0;
void LCSLength( int m , int n , char *x , char *y , char *b ) {
int i , j , k;
int c[max][max];
memset(c,0,sizeof(c));
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 );
fout<<x[i];
total++;
}
else if( b[k] == '|' )
{
LCS( i - 1 , j , x , b , width );
}
else
{
LCS( i , j - 1 , x , b , width );
}
}
int main()
{
char x[max];
// = { 'a' , 'b' , 'c' , 'b' , 'd' , 'a' , 'b' };
char y[max];
// = { 'b' , 'd' , 'c' , 'a' , 'b' , 'a' };
fin>>x>>y;
int m,n;
m = strlen(x);
n = strlen(y);
char b[max];
memset(b,'0',sizeof(b));
LCSLength( m , n , x , y , b );
LCS( m , n , x , b , n );
fout<<endl;
fout<<total<<endl;
return 0;
}。