【开篇案例】
序列:X={A,B,C,B,D,A,B}
序列:Y={B,C,D,B,A}
序列:Z={B,C,D,B}
序列:W={B,C,D}
Z是(X与Y)的最长公共子序列(Longest-Common-Subsequence),而W虽然是X与Y的公共子序列,但是不是最长的!
【晦涩定义】
给定一个序列X={x1,x2,x3...xm},另一个序列Z={z1,z2,z3...zk}为子序列,如果存在X的
一个严格递增下标序列<i1,i2,i3,i4...ik>,使得所有的j=1,2,...,k,有x[i<j>]=z[j]
例如,Z={B,C,D,B}是X={A,B,C,B,D,A,B}一个最长子序列,相应的下标序列为<2,3,5,7>
如果Z同时是Y的最长子序列,那么Z就是X与Y的最长公共子序列
【应用场景】
生物基因的两条螺旋链的相似度,公共子序列越长,越相似!(Z=X=Y的时候,可以说两条螺旋链完全相同!)
【最优子结构性质】
特征1、如果xm==yn,那么zk == xm == yn ,而且Zk-1是(Xk-1与Yk-1)的一个LCS
特征2、如果xm != yn,那么zk !=xm,推出:Z是(Xm-1和Y)的一个LCS
特征3、如果xm != yn,那么zk !=yn,推出:Z是(X与Yn-1)的一个LCS
这三条特征说明两个序列(X与Y)的LCS,包含这两个序列前缀(Xm-1与Yn 或 Xm与Yn-1 ...)的LCS,
也就是说,LCS具备最优子结构性质!
【递归解性质】
1.xm == yn,那么把xm==yn加入到这个LCS中就得到X,Y的LCS
2.xm!=yn,那么找到(Xm-1与Yn)的一个LCS,和(X与Yn-1)的一个LCS,比较哪个更长取之为XY的一个LCS
这两点可以看出,这个LCS有递归解!
【动态规划】
既满足了最优子结构,又满足了递归解,我们很容易就想到了用动态规划来实现!
定义C[i,j]为Xi,Yj的一个LCS长度!
C[i,j]={ 0 , i ==0 || j == 0
c[i-1,j-1]+1 , i,j>0 && xi == yj
max(c[i,j-1],c[i-1,j]) i,j>0 && xi != yj }
【LCS-Length算法】
LCS-LENGTH(X,Y) m = length[X] n = length[Y] for i= 1 to m do c[i,0] = 0 for j =1 to n do c[0,j] = 0 for i =1 to m do for j=1 to n do if xi == yj then c[i,j] = c[i-1,j-1]+1 b[i,j] = 1 else if c[i-1,j] >= c[i,j-1] then c[i,j] =c[i-1,j] b[i,j] = 2 else then c[i,j] = c[i,j-1] b[i,j] = 3 return c and b
【LCS构造算法】
b[i,j] == 1 表示xi == yi是LCS的一个元素
b[i,j] == 2 表示LCS选择的是c[i-1,j]
b[i,j] == 3 表示LCS选择的是c[i,j-1]
PRINT-LCS(b,X,i,j) if i == 0 || j == 0 then return if b[i,j] == 1 then PRINT-LCS(b,X,i-1,j-1) print xi else if b[i,j] == 2 then PRINT-LCS(b,X,i-1,j) else then PRINT-LCS(b,X,i,j-1)
【C++代码】
#include <iostream> #define length 7 using namespace std; int c[length+1][length+1],b[length+1][length+1]; int x_length; int y_length; void Length_LCS(int x[],int y[]) { int m=x_length; int n=y_length; for (int i=0; i<=m; i++) { c[i][0] = 0; } for(int j=0; j<=n; j++) { c[0][j] =0; } for(int i=1; i<=m; i++) { for(int 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 print_LCS(int b[length+1][length+1],int X[length+1],int i,int j) { if( i== 0|| j ==0) return ; if(b[i][j] == 1) { print_LCS(b,X,i-1,j-1); cout<<X[i]<<" "; } else if(b[i][j] == 2) { print_LCS(b,X,i-1,j); } else { print_LCS(b,X,i,j-1); } } int main() { int X[]= {0,1,2,3,2,4,1,2}; int Y[]= {0,2,4,3,1,2,1}; x_length=7; y_length=6; cout<<"This is to test the LCS by ouyang!"<<endl; Length_LCS(X,Y); print_LCS(b,X,x_length,y_length); return 0; }
【与最长公共子序列的关系】
序列X与排完序的X'的最长公共子序列 == 最长单调递增子序列!
重点:排完序的自己!
【C++代码】
#include <iostream> using namespace std; void Exchange(int *a,int *b) { int t=*a; *a=*b; *b=t; } int Position(int a[],int p,int r) { int t=a[r]; int i=p-1; bool flag =true; for(int j=p;j<=r-1;j++) { if(a[j]!=t) { flag=false; } if(a[j] <= t) { i++; Exchange(&a[j],&a[i]); } } Exchange(&a[i+1],&a[r]); if(!flag) return i+1; else return (p+r)/2; } void QuickSort(int a[],int p,int r) { if(p < r) { int q=Position(a,p,r); QuickSort(a,p,q-1); QuickSort(a,q+1,r); } } #define length 7 int c[length+1][length+1],b[length+1][length+1]; int x_length; int y_length; void Length_LCS(int x[],int y[]) { int m=x_length; int n=y_length; for (int i=0; i<=m; i++) { c[i][0] = 0; } for(int j=0; j<=n; j++) { c[0][j] =0; } for(int i=1; i<=m; i++) { for(int 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 print_LCS(int b[length+1][length+1],int X[length+1],int i,int j) { if( i== 0|| j ==0) return ; if(b[i][j] == 1) { print_LCS(b,X,i-1,j-1); cout<<X[i]<<" "; } else if(b[i][j] == 2) { print_LCS(b,X,i-1,j); } else { print_LCS(b,X,i,j-1); } } int main() { cout<<"This is to test the 最长子序列 by ouyang!"<<endl; int X[]= {NULL,1,2,3,2,4,1,2}; int Y[]= {NULL,1,2,3,2,4,1,2}; x_length=7; y_length=7; QuickSort(Y,1,7); Length_LCS(X,Y); print_LCS(b,X,x_length,y_length); return 0; }