最长公共子序列

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值