最长公共子序列

来源:互联网 发布:天地图数据分级标准 编辑:程序博客网 时间:2024/05/17 06:23

一个给定的子序列是在该序列中删去若干元素的得到的序列。确切的说,若给定序列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);}}



0 0