最长子序列之LCSlength算法详解
- 格式:doc
- 大小:425.50 KB
- 文档页数:11
一,最长递增子序列问题的描述设L=<a1,a2,…,a n>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<a K1,a k2,…,a km>,其中k1<k2<…<k m且a K1<a k2<…<a km。
求最大的m值。
二,第一种算法:转化为LCS问题求解设序列X=<b1,b2,…,b n>是对序列L=<a1,a2,…,a n>按递增排好序的序列。
那么显然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<i且a j<a i。
如果这样的元素存在,那么对所有a j,都有一个以a j为末元素的最长递增子序列的长度f(j),把其中最大的f(j)选出来,那么f(i)就等于最大的f(j)加上1,即以a i为末元素的最长递增子序列,等于以使f(j)最大的那个a j为末元素的递增子序列最末再加上a i;如果这样的元素不存在,那么a i自身构成一个长度为1的以a i为末元素的递增子序列。
试验四.最长公共子序列(LCS)算法一.实验原理对于给定的两个序列A和B,如果序列C既是A的子序列,又是B的子序列,则称C是A和B的公共子序列,A和B的公共子序列可能不止一个,其中最长的那个序列称为公共子序列。
公共子序列在很多实际应用中起关键作用。
序列A={abdledefiess},B={abwdifgdefiesa},最长公共子序列为C={defies}二.实验目的本次实验就是要找出两个序列XY的最长公共子序列LCS三.实验步骤1.查找公共子序列2.输出公共子序列核心算法代码如下:int **lcs_length(char p[],char q[],int **c,int **k,int m,int n){int i,j;for(i=1;i<=m;i++){for(j=1;j<=n;j++){if(p[i-1]==q[j-1])//如果两个字母相等的情况{c[i][j]=c[i-1][j-1]+1;k[i][j]=1;}else{if(c[i-1][j]>=c[i][j-1])//两字母不等情况1{c[i][j]=c[i-1][j];k[i][j]=2;}else//两字母不等情况2{c[i][j]=c[i][j-1];k[i][j]=3;}}}}return c,k;}输出代码void print_lcs(int **k,char p[],int i,int j){if(i==0||j==0)return ;if(k[i][j]==1){print_lcs(k,p,i-1,j-1);//通过递归的方法按照输入的从头到尾的顺序输出LCScout<<p[i-1];}else if(k[i][j]==2)print_lcs(k,p,i-1,j);elseprint_lcs(k,p,i,j-1);}四.实验结果根据实验算法运行结果如下:以上算法表明可以正确的找出两个序列的最长公共子序列,达到了本次实验的目的.。
最长公共子序列算法最长公共子序列算法概述最长公共子序列(Longest Common Subsequence,LCS)是一种常见的字符串匹配问题。
给定两个字符串S和T,求它们的最长公共子序列,即在S和T中都出现的最长的子序列。
该问题可以用动态规划算法解决。
算法原理动态规划算法是一种将复杂问题分解成更小的子问题来解决的方法。
在LCS算法中,我们将两个字符串S和T分别看作X和Y,并定义一个二维数组c[i][j]表示X[1..i]和Y[1..j]的LCS长度。
则有以下递推公式:c[i][j] = 0, if i=0 or j=0c[i][j] = c[i-1][j-1]+1, if X[i]=Y[j]c[i][j] = max(c[i-1][j], c[i][j-1]), if X[i]!=Y[j]其中第一行和第一列均初始化为0,因为空字符串与任何字符串的LCS长度均为0。
当X[i]=Y[j]时,说明当前字符相同,那么当前字符可以加入到LCS中,所以LCS长度加1;否则当前字符不能加入到LCS中,则需要从上一个状态继承得到当前状态。
最终结果即为c[m][n],其中m和n分别表示X和Y的长度。
算法实现以下是LCS算法的Python实现:def lcs(X, Y):m = len(X)n = len(Y)c = [[0] * (n+1) for i in range(m+1)]for i in range(1, m+1):for j in range(1, n+1):if X[i-1] == Y[j-1]:c[i][j] = c[i-1][j-1] + 1else:c[i][j] = max(c[i-1][j], c[i][j-1])return c[m][n]其中X和Y分别为两个字符串。
算法优化以上算法的时间复杂度为O(mn),其中m和n分别表示X和Y的长度。
如果X和Y较长,算法会很慢。
但是我们可以通过一些优化来降低时间复杂度。
利用递归求最长公共子序列问题1.引言最长公共子序列(Longest Common Subsequence,简称LCS)是一个经典的动态规划问题,其可以描述为:给定两个序列X和Y,求出它们的最长公共子序列的长度。
在计算机科学领域,LCS问题广泛应用于字符串比较、文本相似度计算、基因序列比对等领域,因此对于LCS问题的求解具有重要的意义。
2.问题描述对于序列X和Y,它们的最长公共子序列可以定义为:如果一个序列Z既是X的子序列又是Y的子序列,且Z的长度最大,那么Z称为X和Y的最长公共子序列。
3.递归解法我们可以使用递归的方式来解决LCS问题。
递归的思想是将原问题划分为更小的子问题求解,然后将子问题的解合并起来得到原问题的解。
对于LCS问题,我们可以将其划分为更小的子问题求解。
假设X和Y的长度分别为m和n,我们可以考虑X中的第m个元素和Y中的第n 个元素是否相等,从而将原问题划分为以下三种情况:a) 如果X[m]等于Y[n],那么X和Y的最长公共子序列就是X[1..m-1]和Y[1..n-1]的最长公共子序列再加上X[m]。
b) 如果X[m]不等于Y[n],那么X和Y的最长公共子序列就是X[1..m-1]和Y[1..n]的最长公共子序列,或者是X[1..m]和Y[1..n-1]的最长公共子序列,取两者的最大值。
c) 如果X或Y的长度为0,那么它们的最长公共子序列的长度为0。
4.递归求解步骤基于上述的划分情况,我们可以得到递归求解LCS问题的步骤如下:a) 如果X和Y的最后一个元素相等,那么LCS(X, Y, m, n) = 1 + LCS(X, Y, m-1, n-1);b) 如果X和Y的最后一个元素不相等,那么LCS(X, Y, m, n) = max(LCS(X, Y, m-1, n), LCS(X, Y, m, n-1));c) 如果m等于0或n等于0,那么LCS(X, Y, m, n) = 0。
最长公共子序列lcs算法最长公共子序列(Longest Common Subsequence,简称LCS)算法是一种常用的字符串匹配算法,用于在两个字符串中找到最长的公共子序列。
在计算机科学领域,字符串匹配是一项基础性的任务,常用于文本比较、版本控制、DNA序列比对等领域。
LCS算法的基本思想是通过动态规划的方式,从头开始比较两个字符串的每个字符,逐步构建一个二维数组来保存公共子序列的长度。
具体步骤如下:1. 创建一个二维数组dp,大小为两个字符串长度加1。
dp[i][j]表示字符串1的前i个字符和字符串2的前j个字符的最长公共子序列的长度。
2. 初始化dp数组的第一行和第一列,即dp[0][j]和dp[i][0]都为0,表示一个空字符串与任何字符串的最长公共子序列长度都为0。
3. 从字符串的第一个字符开始,逐行逐列地比较两个字符串的字符。
如果两个字符相等,则说明这个字符属于最长公共子序列,将dp[i][j]的值设置为dp[i-1][j-1]+1。
如果两个字符不相等,则说明这个字符不属于最长公共子序列,取dp[i-1][j]和dp[i][j-1]中的较大值来更新dp[i][j]的值。
4. 最后,dp[m][n]即为两个字符串的最长公共子序列的长度,其中m和n分别为两个字符串的长度。
接下来,我们通过一个例子来演示LCS算法的具体过程。
假设有两个字符串str1="ABCDAB"和str2="BDCABA",我们要找出这两个字符串的最长公共子序列。
创建一个二维数组dp,大小为(str1.length()+1)×(str2.length()+1)。
初始化dp数组的第一行和第一列为0。
```B DC A B AA 0 0 0 0 0 0B 0C 0D 0A 0B 0```从第一个字符开始比较,我们发现str1[1]和str2[1]都是B,因此dp[1][1]=dp[0][0]+1=1。
最长公共子序列问题LCS问题描述一个给定序列的子序列是在该序列中删去若干元素后得到的序列。
确切地说,若给定序列X=<x1, x2,…, x m>,则另一序列Z=<z1, z2,…, z k>是X的子序列是指存在一个严格递增的下标序列<i1, i2,…, i k>,使得对于所有j=1,2,…,k有例如,序列Z=<B,C,D,B>是序列X=<A,B,C,B,D,A,B>的子序列,相应的递增下标序列为<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>,则序列<B, C, A>是X和Y的一个公共子序列,序列<B, C, B, A>也是X和Y的一个公共子序列。
而且,后者是X和Y的一个最长公共子序列,因为X和Y没有长度大于4的公共子序列。
最长公共子序列(LCS)问题:给定两个序列X=<x1, x2, …, x m>和Y=<y1, y2, … , y n>,要求找出X和Y的一个最长公共子序列。
参考解答动态规划算法可有效地解此问题。
下面我们按照动态规划算法设计的各个步骤来设计一个解此问题的有效算法。
1.最长公共子序列的结构解最长公共子序列问题时最容易想到的算法是穷举搜索法,即对X的每一个子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列,并且在检查过程中选出最长的公共子序列。
X的所有子序列都检查过后即可求出X和Y的最长公共子序列。
X 的一个子序列相应于下标序列{1, 2, …, m}的一个子序列,因此,X共有2m个不同子序列,从而穷举搜索法需要指数时间。
事实上,最长公共子序列问题也有最优子结构性质,因为我们有如下定理:定理: LCS的最优子结构性质设序列X=<x1, x2, …, x m>和Y=<y1, y2, …, y n>的一个最长公共子序列Z=<z1, z2, …,z k>,则: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的最长公共子序列;3.若x m≠y n且z k≠y n,则Z是X和Y n-1的最长公共子序列。
问题描述:一个给定序列的子序列是在该序列中删去若干元素后得到的序列。
确切地说,若给定序列X= { x1, x2,…, x m},则另一序列Z= {z1, z2,…, z k}是X的子序列是指存在一个严格递增的下标序列{i1, i2,…, i k},使得对于所有j=1,2,…,k有X ij=Z j。
例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{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},则序列{B,C,A}是X和Y的一个公共子序列,序列{B,C,B,A}也是X和Y的一个公共子序列。
而且,后者是X和Y的一个最长公共子序列,因为X和Y没有长度大于4的公共子序列。
给定两个序列X= {x1, x2, …, x m}和Y= {y1, y2, … , y n},要求找出X和Y的一个最长公共子序列。
问题解析:设X= { A, B, C, B, D, A, B},Y= {B, D, C, A, B, A}。
求X,Y的最长公共子序列最容易想到的方法是穷举法。
对X的多有子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列。
由集合的性质知,元素为m的集合共有2^m个不同子序列,因此,穷举法需要指数级别的运算时间。
进一步分解问题特性,最长公共子序列问题实际上具有最优子结构性质。
设序列X={x1,x2,……x m}和Y={y1,y2,……y n}的最长公共子序列为Z={z1,z2,……z k}。
则有:(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的最长公共子序列。
动态规划经典——最长公共⼦序列问题(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)。
首先将要看到如何运用动态编程查找两个DNA 序列的最长公共子序列(longest common subsequence,LCS)。
发现了新的基因序列的生物学家通常想知道该基因序列与其他哪个序列最相似。
查找LCS 是计算两个序列相似程度的一种方法:LCS 越长,两个序列越相似。
子序列中的字符与子字符串中的字符不同,它们不需要是连续的。
例如,ACE是ABCDE的子序列,但不是它的子字符串。
请看下面两个DNA 序列:∙S1 = DE>GCCCTAGCGDE>∙S2 = DE>GCGCAATGDE>这两个序列的LCS 是GCCAG。
(请注意,这仅是一个LCS,而不是唯一的LCS,因为可能存在其他长度相同的公共子序列。
这种最优化问题和其他最优化问题的解可能不止一个。
)LCS 算法首先,考虑如何递归地计算LCS。
令:∙C1是S1最右侧的字符∙C2是S2最右侧的字符∙S1'是S1中“切掉” C1的部分∙S2'是S2中“切掉” C2的部分有三个递归子问题:∙L1 = LCS(S1', S2)∙L2 = LCS(S1, S2')∙L3 = LCS(S1', S2')结果表明(而且很容易使人相信)原始问题的解就是下面三个子序列中最长的一个:∙L1∙L2∙如果C1等于C2,则为L3后端加上C1,如果C1不等于C2,则为L3。
(基线条件(base case)是S1或S2为长度为零的字符串的情形。
在这种情况下,S1和S2的LCS 显然是长度为零的字符串。
)但是,就像计算斐波纳契数的递归过程一样,这个递归解需要多次计算相同的子问题。
可以证明,这种递归解法需要耗费指数级的时间。
相比之下,这一问题的动态编程解法的运行时间是Θ(mn),其中m和n分别是两个序列的长度。
为了用动态编程有效地计算LCS,首先需要构建一个表格,用它保存部分结果。
沿着顶部列出一个序列,再沿着左侧从上到下列出另一个序列,如图2 所示:图2. 初始LCS 表格这种方法的思路是:将从上向下、从左到右填充表格,每个单元格包含一个数字,代表该行和该列之前的两个字符串的LCS 的长度。
求解所有最长公共子序列一、问题分析该部分思路同课件二、算法设计思路根据问题分析的结果,具体的算法设计思路如下:1)申明两个数组,用于保存比较的两个字符串;由于事先不知字符串大小,故动态的实现,这里用C++的容器。
2)申明全局变量,二维数组B和数组C。
数组C用于保存计算Xi和Yi的LCS值;数组B保存当前的C是从哪个子问题得来的。
为此,定义一个枚举类型,用于标识不同的方向,分别为对角线、向上、向左和向左向上四个方向。
3)根据动态规划,实现一个函数LCS_LENGTH,完成的功能是计算数组B和C。
具体过程是:先是动态申请二维数组B和C,他们的行列长度都增加1,目的就是方便计算。
将C的第0行和第0列都赋上0,即初始化。
开始计算C[i][j],以行为主,一次计算C的每一个元素,即将两个数组逐一比较。
比较时就有两种情况,分别是若相等时,就将C[i][j]设置成C[i-1][j-1],同时将B[i][j]设置成DIAGONAL。
若不相等时,比较C[i-1][j] 和C[i][j-1]的值,又有三种情况:一是C[i-1][j] 与C[i][j-1]相等,就随便把某一个赋给C[i][j],比如C[i-1][j],B[i][j]设置为UP_LEFT;二是若C[i-1][j] 大于C[i][j-1],则将C[i-1][j]赋给C[i][j],并且将B[i][j]设置成UP;最后是若C[i-1][j] 小于C[i][j-1],则将C[i][j-1]赋给C[i][j],并且将B[i][j]设置成LEFT。
4)根据第3)步骤的结果,就可以找出所有LCS了。
这里会用到回溯方法,具体实现可以用栈,也可以用递归。
本人使用的是递归,代码简单、易懂。
具体实现方法是:申请一个数组用于保存一个LCS,这个数组会反复使用,因此,一旦找到一个就会立即将它输出。
再设置一个变量curpos标识当前的数组下标,一个变量len 保存当前LCS数组的元素个数。