题目
最长公共子序列问题是在序列X和Y的公共子序列中查找长度最长的公共子序列,而最长公共子序列往往不止一个。
例如: X = ( A , B , C , B , D , A , B ) , Y = ( B , D , C , A , B , A ) , 则 Z = ( B , C , B , A ) , Z 1 = ( B , C , A , B ) , Z 2 = ( B , D , A , B ) , 均 属 于 L C S ( X , Y ) , 即 X , Y 的 最 长 公 共 子 序 列 有 3 个 。 X= (A,B,C,B,D,A,B),Y = (B,D,C,A,B,A),则Z = (B,C,B,A),Z_1 = (B,C,A,B),Z_2 = (B,D,A,B),均属于LCS(X,Y),即X,Y的最长公共子序列有3个。 X=(A,B,C,B,D,A,B),Y=(B,D,C,A,B,A),则Z=(B,C,B,A),Z1=(B,C,A,B),Z2=(B,D,A,B),均属于LCS(X,Y),即X,Y的最长公共子序列有3个。
定理: LCS的最优子结构性质
设序列: X = ( x 1 , x 2 , . . . . , x n ) , Y = ( y 1 , y 2 , . . . , y n ) 的 一 个 最 长 公 共 子 序 列 Z = z 1 , z 2 , . . . , z n X= (x_1,x_2,....,x_n),Y = (y_1,y_2,...,y_n)的一个最长公共子序列Z={z_1,z_2,...,z_n} X=(x1,x2,....,xn),Y=(y1,y2,...,yn)的一个最长公共子序列Z=z1,z2,...,zn,则:
(1)若 x m = y n , 则 z k = x m = y n , 且 Z k − 1 是 X m − 1 和 Y n − 1 x_m = y_n,则z_k=x_m=y_n,且Z_{k-1}是X_{m-1}和Y_{n-1} xm=yn,则zk=xm=yn,且Zk−1是Xm−1和Yn−1的最长公共子序列;
(2)若 x m = ̸ y n , 则 z k = ̸ x m , 则 Z 是 X m − 1 和 Y x_m =\not y_n,则z_k =\not x_m,则Z是X_{m-1}和Y xm=yn,则zk=xm,则Z是Xm−1和Y的最长公共子序列;
(3)若 x m = ̸ y n , 则 z k = ̸ y n , 且 Z 是 X 和 Y n − 1 x_m =\not y_n,则z_k=\not y_n,且Z是X和Y_{n-1} xm=yn,则zk=yn,且Z是X和Yn−1的最长公共子序列。
建立递归关系
我们定义c[i,j]记录序列 X i X_i Xi和 Y j Y_j Yj的最长公共子序列的长度。
状态转移方程为:
c [ i ] [ j ] = { 0 i = 0 , j = 0 c [ i − 1 ] [ j − 1 ] + 1 i , j > 0 ; x i = y i max { c [ i ] [ j − 1 ] , c [ i − 1 ] [ j ] } i , j > 0 ; x i = ̸ y i c[i][j] = \begin{cases} 0 & i = 0,j = 0 \\ c[i-1][j-1]+1 & i,j>0;x_i = y_i \\ \max\ \{{c[i][j-1], c[i-1][j]}\} & i,j>0;x_i =\not y_i \end{cases} c[i][j]=⎩⎪⎨⎪⎧0c[i−1][j−1]+1max {c[i][j−1],c[i−1][j]}i=0,j=0i,j>0;xi=yii,j>0;xi=yi
代码
#include<stdio.h>
#define NUM 100
int c[NUM][NUM];//记录序列Xi和Yj的最长公共子序列的长度
int b[NUM][NUM];//用来记录比较情况
void LCSLength(int m, int n, const char x[],char y[])//计算最优值
{
int i,j;
for (i = 1; i <= m; i++) c[i][0] = 0;//第0列全为0(此时Y序列长度为0)
for (i = 1; i <= n; i++) c[0][i] = 0;//第0行全为0(此时X序列长度为0)
for (i = 1; i <= m; i++)
for (j = 1; j <= n; j++)
{
if (x[i]==y[j]) //①i,j>0且Xi与Yj相等
{
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}else if (c[i-1][j]>=c[i][j-1]) {//②max c[i-1][j],i,j>0,Xi不等于Yj
c[i][j]=c[i-1][j];
b[i][j]=2;
}else { //③max c[i][j-1],i,j>0,Xi不等于Yj
c[i][j]=c[i][j-1];
b[i][j]=3;
}
}
}
void LCS(int i,int j,char x[])// 构造最优解结构(递归)
{
if (i ==0 || j==0) return;
if (b[i][j]== 1){ LCS(i-1,j-1,x); printf("%c",x[i]); }
else if (b[i][j]== 2) LCS(i-1,j,x);
else LCS(i,j-1,x);
}
int main()
{
char x[NUM];
char y[NUM];
int m, n;
scanf("%d\n", &m);
for (int i=1; i<=m; i++)//读取X序列
scanf("%c", &x[i]);
scanf("%d\n", &n);
for (int i=1; i<=n; i++)//读取Y序列
scanf("%c", &y[i]);
LCSLength(m, n, x, y);
printf("%d\n", c[m][n]);//输出最优值
LCS(m,n,x);//输出最优解结构(此时x和y都无所谓)
return 0;
}
输入
7
ABCBDAB
6
BDCABA
输出
4
BCBA
总结
怎么讲呢,学到了很多Markdown公式的写法,而矩阵连乘积正是动态规划的一个经典例子。学好动态是算法的一个深化吧!