动态规划——最长公共子序列LCS

【算法导论 ch15-4】

首先确定什么是最长公共子序列:

最长公共子串(longest common substring) :其中的子串是原串的一个连续的部分;

最长公共子序列(longest common subsequence):是不改变序列的顺序,从序列中得到任意的元素从而获得的新序列

二者区别=>子串中字符位置必须连续,子序列可以不必连续

(由于序列元素顺序的不同,得到的LCS可能不止一个)

用动态规划方法求解,需要先寻找最优子结构


定义c[i,j]为序列Xi和Yj的LCS长度,递归求解,将其保存到二维数组C中。可得递归式:


这样得到的表中最后一个值就是Xm和Yn的LCS;

还需要一个二维数组b,用于保存c[i,j]中的值是从c[i-1,j-1],c[i-1,j]还是c[i,j-1]中计算得到的,便于反向查找LCS元素时找到元素位置

伪代码:


便于反向查找LCS元素时找到元素位置

算导上的数组示意图:


构造LCS序列时,用b数组,从b[m,n]开始,延箭头反向追踪下去,每当遇到斜向尽头都证明该处对应的Xi和Yj是相同的,且是属于LCS的。一直追踪直到i或j到达最小。

为了保存下来每一个LCS,增加了一个数组用于保存结果,完整代码如下:

#include <stdio.h>
#include <string>
#include <math.h>
using namespace std;
/*可以考虑不适用b数组,直接从c[i-1,j-1],c[i-1,j],c[i,j-1]中计算得到c[i,j]运算方向*/
const string str_x = "BDCABA";//"ACCGGTCGAGTGCGCGGAAGCCGGCCGAA";//
const string str_y = "ABCBDAB";//"GTCGTTCGGAATGCCGTTGCTCTGTAAA";//

int **c;
int **b;
char *z; 
/*
建立的二维数组c和b的维数应当分别比序列x和y多1,用于存放初始的0值;
所以在c和b数组中存放的值,对应序列x和y要大1,下标容易弄错

代码输出一种LCS,但LCS不一定只有1种
*/
int count;
int lenLCS;
int getsublength(string x,string y,int m,int n)
{
	int i,j;
	//初始化
	for(i = 0;i<m;i++)
	{
		c[i][0] = 0;
		b[i][0] = 0;
	}
	for(i = 0;i<n;i++)
	{
		c[0][i] = 0;
		b[0][i] = 0;
	}
	//填写表格 ==> 表格c[i,j]是序列Xi和Yj的一个LCS的长度,用于得到LCS最终长度
	//b[i,j] ==>用来记录数组中箭头的方向,便于反向查找LCS元素时找到元素位置
	for (i=1;i<m ;i++ )
	{
		for (j = 1;j<n ;j++ )
		{
			if(x[i-1]==y[j-1])//斜角==3
			{
				c[i][j] = c[i-1][j-1] + 1;//LCS长度加上1
				b[i][j] = 3;
			}
			else if(c[i-1][j]==c[i][j-1])左和上相同==4 ==> 需要当做两种情况处理
			{
				c[i][j] = c[i-1][j];
				b[i][j] = 4;
			}
			else if(c[i-1][j]>c[i][j-1])//上==2
			{
				c[i][j] = c[i-1][j];
				b[i][j] = 2;
			}
			else if(c[i-1][j]<c[i][j-1])//左==1
			{
				c[i][j] = c[i][j-1];
				b[i][j] = 1;
			} 
		}
	}
	return c[m-1][n-1];
}
void getLCS(int x,int y,int k,char **ans)//增加一个数组保存结果,存下每一次运行的结果
{
	if( (x==0||y==0)&& k == 1 ) 
	{
		int t;
		count++;
		for (t = 1;t<lenLCS ;t++ )
		{
			//printf("%c",z[t]);
			ans[count][t] = z[t];
		}
		//printf("%d",k);
		//printf("\n");
		return;
	}
	if(b[x][y] == 4)
	{
		//
		getLCS(x-1,y,k,ans);
		getLCS(x,y-1,k,ans);
	}
	else if (b[x][y] == 3)
	{
		k--;
		z[k] = str_x[x-1];
		getLCS(x-1,y-1,k,ans);
	}
	else if(b[x][y] == 2) 
		getLCS(x-1,y,k,ans);
	else if(b[x][y] == 1) 
		getLCS(x,y-1,k,ans); 
	//return;
}
void printarray(int **A,int x,int y)
{
	int i,j;
	for (i = 0;i<x ;i++ )
	{
		for (j = 0;j<y ;j++ )
		{
			printf ("%3d",A[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int i,j;
	count = 0;
	int len_x = str_x.length();
	int len_y = str_y.length();

	c = new int* [len_x+1];
	b = new int *[len_x+1];
	for (i = 0;i<len_x+1 ;i++ )
	{
		c[i] = new int [len_y+1];
		b[i] = new int [len_y+1];
	}
	int sublen = getsublength(str_x,str_y,len_x+1,len_y+1);
	lenLCS = sublen + 1;
	printf("length of LCS = %d\n",sublen);

	printarray(c,len_x+1,len_y+1);
	printf("\n");
	printarray(b,len_x+1,len_y+1);

	z = new char [sublen+1];
	char** ans = new char *[len_x+1];
	for (i = 0;i<len_x ;i++ )
	{
		ans[i] = new char [sublen+1];
	}
	getLCS(len_x,len_y,sublen+1,ans);
	/*
	for (i = 1;i<sublen+1 ;i++ )
	{
		printf("%c",z[i]);
	}
	printf("\n");
	*/
	printf("\n%d kinds of LCS:\n",count);
	for (i = 1;i<=count ; i++)
	{
		for (j = 1;j<=sublen ;j++ )
		{
			printf("%c",ans[i][j]);
		}
		printf("\n");
	}
	/* 
	printf("%s\n",z);*/
	delete [] z;
	for (i=0;i<len_x+1 ;i++)
	{
		delete [] c[i];
		delete [] b[i];
		delete [] ans[i];
	}
	delete [] c;
	delete [] b;
	delete [] ans;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值