一个给定的子序列是在该序列中删去若干元素的得到的序列。确切的说,若给定序列X={x1,x2,…,xm}则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…ik}使得对于所有j=1,2,..k有zj=xi,例如序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序,相应的递增下标序列为{2,3,5,7}。
给定两个序列X和Y,当另一序列Z既是X的子序列又是X的子序列时,称Z是序列X和Y的公共子序列。
最长子序列问题:给定两个序列X={x1,x2,…,xm}和另一序列Z={z1,z2,…,zk}找出X和Z的一个最长公子序列。
动态规划算法可有效的解决问题。下面我们按照动态规划算法来设计一个有效的算法
1.算法步骤
(1)最长公共子序列的结构
解最长公共子序列问题具有最优子结构性质
设序列X={x1,x2,…,xm} 和Y={y1,y2,…,ym}的一个最长公共子序列为Z={z1,z2,…,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的最长公共子序列
(2) 子序列递归问题
由最长公共子序列问题的最优子结构性质可知,要找出X={x1,x2,…,xm}和Y={y1,y2,…,ym}的最长公共子序列,可按以下方式递归地进行:定Xm=Yn时,找出Xm-1和Yn-1的公共最长子序列,然后在其尾部加上Xm=Yn,即可得到X和Y的一个公共最长子序列当Xm!=Yn时,必须解两个子问题,即找出Xm和Y的一个公共最长子序列及X和Yn的一个公共最长子序列这两个公共最长子序列最长者即为X和Y的一个公共最长子序列。
(3) 计算最优值
直接利用递归容易写出一个计算C[i][j]的递归算法,但其计算时间是随输入的长度指数增长的,由于在所考虑的子问题空中,总共有O(mn)个问题的子问题,因此,用动态规划算法自底向上的计算。
计算最长公共子序列长度的动态规划算法LCSLength一序列X={x1,x2,…,xm}和Y={y1,y2,…,ym}作为输入,输出两个数组C和b.其中c[i][j]存储Xi和Yj的最长公共子序列的长度,b[i][j]记录c[i][j]的值是由哪一个子问题的解得到的,这在构造最长公共子序列要用到。问题的最优值,即X和y的最长公共子序列记录与c[m][n]
(4) 构造最长公共子序列
由算法LCSLength计算得到的数组b可用于快速构造X={x1,x2,…,xm} 和Y={y1,y2,…,ym}的最长公共子序列,首先从b[m][n]开始,沿着其方向在b中搜索,当遇到1时表示Xi和Yj的最长公共子序列是由Xi-1和Yj-1的最长公共子序列在尾部加上Xi所得到的子序列。当遇到2时表示Xi和Yj的最长公共子序列是由Xi-1和Y的最长公共子序列在尾部加上Xi所得到的子序列。当遇到3时表示Xi和Yj的最长公共子序列是由X和Yj-1的最长公共子序列在尾部加上Xi所得到的子序列
下面是代码亲测通过
/********************************
最长公共子序列
输入:两个序列
输出:两个序列的公共子序列
*********************************/
#include<stdio.h>
void LCSLength(int m,int n,char * x,char * y,int c[20][20],int b[20][20]);
int LCS(int i,int j,char * x,int b[20][20]);
int main()
{
int i=0;
char X[20]={0}; //X表示第一个字符串
char Y[20]={0}; //Y表示第二个字符串
int C[20][20]={0}; //存取X,Y最长公共子序列的长度
int B[20][20]={0}; //记录C中的值是由哪一个子问题的解得到的
int XLength=0; //X字符串的长度
int YLength=0; //Y字符串的长度
printf("请输入第一个字符串\n");
scanf("%s",X);
printf("请输入第二个字符串\n");
scanf("%s",Y);
while(X[i]) //计算X字符串的长度
{
XLength++;
i++;
}
i=0;
while(Y[i]) //计算Y字符串的长度
{
YLength++;
i++;
}
printf("\n最长公共子序列是:\n");
LCSLength(XLength,YLength,X,Y,C,B);
LCS(XLength,YLength,X,B);
}
//计算最优值
void LCSLength(int m,int n,char * x,char * y,int c[20][20],int b[20][20])
{
int i,j;
for(i=0;i<=m;i++)
{
c[i][0]=0;
}
for(i=0;i<=n;i++)
{
c[0][i]=0;
}
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];
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;
}
}
}
//构造最长公共子序列
int LCS(int i,int j,char * x,int b[20][20])
{
if(i==0||j==0)
{
return 0;
}
if(b[i][j]==1)
{
LCS(i-1,j-1,x,b);
printf("%c",x[--i]);
}else if(b[i][j]==2)
{
LCS(i-1,j,x,b);
}else{
LCS(i,j-1,x,b);
}
}