动态规划———最长公共子序列(LCS)
最长公共子序列+sdutoj2080改编:
http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Contest/contestproblem/cid/2788/pid/2080
传送门: https://blog.csdn.net/sunshine_pb/article/details/21820159
设序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列为Z={z1,z2,…,zk},
记: Xk为序列X中前k个连续字符组成的子序列,
Yk为序列Y中前k个连续字符组成的子序列,
Zk为序列Z中前k个连续字符组成的子序列,
显然有下式成立:
(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的最长公共子序列。
可见,两个序列的最长公共子序列包含了这两个序列的前缀序列的最长公共子序列。
要找出序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列,可按下述递推方式计算:
当xm=yn时,找出Xm-1和Yn-1的最长公共子序 列,然后在其尾部加上xm即可得到X和Y的最长公共子序列;
当xm≠yn时,必须求解两个子问题:找出Xm-1和Y的最长公共子序列以及Xm和Yn-1 的最长公共子序列,这两个公共子序列中的较长
者即为X和Y的最长公共子序列
设L[i][j]表示子序列Xi和Yj的最长公共子序列的长度,可得如下动态规划函数:
L[0][0] = L[i][0] = L[0][j] = 0 (1≤i≤m,1≤j≤n)
L[i][j]只是记录子序列的长度,要打印得到Xm和Yn具体的最长公共子序列,设二维表S[m+1][n+1],其中S[i][j]表示在计算L[i][j]的过程中的搜索状态,并且有:
若S[i][j]=1,表明ai=bj,则下一个搜索方向是S[i-1][j-1];
若S[i][j]=2,表明ai≠bj且L[i][j-1]≥L[i-1][j],则下一个搜索方向是S[i][j-1];
若S[i][j]=3,表明ai≠bj且L[i][j-1]<L[i-1][j],则下一个搜索方向是S[i-1][j]。
举例:序列X=(a,b,c,b,d,b),Y=(a,c,b,b,a,b, d, b,b),建立两个(m+1)×(n+1)的二维表L和表S,分别存放搜索过程中得到的子序列的长
度和状态。
下面代码为sdutoj2080改编代码:
1 /* */ 2 # include <bits/stdc++.h> 3 using namespace std; 4 5 int CommonOrder( int len1, int len2, char x[], char y[], char z[]); 6 int L[501][501]; 7 int S[501][501]; 8 int main() 9 { 10 char a[501], b[501], z[501]; 11 int i, t, len1, len2; 12 while( gets(a) && gets(b) ) 13 { 14 len1 = strlen(a); 15 len2 = strlen(b); 16 t = CommonOrder(len1, len2, a, b, z); 17 printf("%d\n", t); 18 for( i=1; i<=t; i++ ) 19 { 20 printf("%c%c", z[i], i==t?'\n':' '); 21 } 22 } 23 return 0; 24 } 25 26 int CommonOrder(int m, int n, char x[], char y[], char z[]) 27 { 28 int j, i, k; 29 for( j=0; j<=n; j++ ) 30 { 31 L[0][j] = 0; 32 } 33 for( i=0; i<=m; i++ ) 34 { 35 L[i][0] = 0; 36 } 37 for( i=1; i<=m; i++ ) 38 { 39 for( j=1; j<=n; j++ ) 40 { 41 if( x[i-1]==y[j-1] ) 42 { 43 L[i][j] = L[i-1][j-1]+1; 44 S[i][j] = 1; 45 } 46 else if( L[i][j-1]>=L[i-1][j] ) 47 { 48 L[i][j] = L[i][j-1]; 49 S[i][j] = 2; 50 } 51 else 52 { 53 L[i][j] = L[i-1][j]; 54 S[i][j] = 3; 55 } 56 } 57 } 58 i = m; 59 j = n; 60 k = L[m][n]; 61 while( i>=0 && j>=0 ) 62 { 63 if( S[i][j]==1 ) 64 { 65 z[k] = x[i-1]; 66 k--; 67 i--; 68 j--; 69 } 70 else if( S[i][j]==2 ) 71 { 72 j--; 73 } 74 else 75 { 76 i--; 77 } 78 } 79 return L[m][n]; 80 }
算法分析:在算法中,
第一个for循环的时间性能是O(n);
第二个for循环的时间性能是O(m);
第三个循环是两层嵌套的for循环,其时间性能是O(m×n);
第四个for循环的时间性能是O(k),而k≤min{m,n},所以,算法的时间复杂性是O(m×n)。