公共子序列问题

5 篇文章 0 订阅
4 篇文章 0 订阅

关于公共子序列,由浅入深,可以有多种问题:

1.最长子序列的长度(或者同时返回最长公共子序列);

2.从序列A变换到序列B,最少需要多少步操作(或者说A与B的距离)。操作包括:1).删除一个字符 2).插入一个字符 3).修改一个字符为另一个字符


一、针对第一个问题,网络上有各种文章介绍了解决方法,这里只介绍最优的方法(动态规划法)

下面是西山居游戏程序类2013校园招聘的笔试原题:

/*函数原型 int GetCommonStr(const char * szStr1, const char * szStr2, char * szCommon, int nSize);
功能说明:找出两个字符串szStr1,szStr2中最大公共子字符串szCommon,szCommon的缓存大小为nSize。
要求:给出该函数的实现,并尽可能提高函数的健壮性*/
int GetCommonStr(const char * szStr1, const char * szStr2, char * szCommon, int nSize)
{
	if(szStr1==NULL || szStr2==NULL || nSize<=0)
		return 0;
	int lcs = 0;
	int *c = new int[strlen(szStr2)];
	const char * index = szStr2;
	for(int i=0; i<strlen(szStr1); i++)
	{
		for(int j=strlen(szStr2)-1; j>=0; j--)
		{
			if(*(szStr1+i)==*(szStr2+j))
			{
				if(i==0||j==0)
					c[j]=1;
				else
					c[j]=c[j-1]+1;
				if(lcs<c[j])
				{
					lcs = c[j];
					index = szStr2+j+1-lcs;//定位最长序列的起点
				}
			}
			else c[j]=0;
		}
	}
	int maxlen=( lcs<nSize? lcs:nSize);
	for(int i=0; i<maxlen; i++)
		*(szCommon+i)=*(index+i);
	*(szCommon+maxlen)='\0';
	return lcs;
}

使用动态规划方法,时间复杂度是O(n1+n2);空间复杂度是O(n2)。其中n1、n2分别是字符串szStr1、szStr2的长度


二、第二个问题需要再第一个问题的基础上进行扩展。

这里就需要一个n1×n2的矩阵R,保存字符串szStr1、szStr2的动态关系。通过关系矩阵R,递归的求出两个字符串中所有的相对最长的公共子序列的长度之和。

比如说,第一次求出最长公共子序列,和子序列的长度,那么这个最长公共子序列把两个字符串szStr1、szStr2分别分割成了三段:头部、尾部和公共子序列。

然后递归的对两个字符串头部,尾部分别求最长公共子序列,直到没有公共子序列或者字符串长度为0为止。

下面是递归函数部分:

int MostCommonStrs(int **R, int row1, int row2, int col1, int col2)
{
	if(row1>row2||col1>col2)
		return 0;
	int max=0;
	int row, col;
	for(int i=row2; i>=row1; i--)
	{
		for(int j=col2; j>=col1; j--)
			if(max<R[i][j])
			{
				max=R[i][j];
				row=i;col=j;
			}
	}
	if(max==0)
		return 0;
	return max + MostCommonStrs( a, row1, row-max, col1, col-max) + MostCommonStrs( a, row+1, row2, col+1, col2);
}

下面代码是在第一个问题过程中,保存关系矩阵R,然后再递归的求头部和尾部的最长公共子序列:

int MostCommonStrs(const char * szStr1, const char * szStr2, char * szCommon, int nSize)
{
	if(szStr1==NULL || szStr1==NULL || nSize<=0)
		return 0;

	int size1 = strlen(szStr1);
	int size2 = strlen(szStr2);

	if(size1<=0 || size2<=0)
		return 0;

	int lcs=0;
	int index1, index2;

	int *c = new int[size2];

	int **R = new int*[size1];// size1×size2的矩阵
	for(int i=0; i<size1; i++)
		R[i] = new int[size2];

	for(int i=0; i<size1; i++)
	{
		for(int j=size2-1; j>=0; j--)
		{
			if(szStr1[i]==szStr2[j])
			{
				if(i==0||j==0)
					c[j]=1;
				else
					c[j]=c[j-1]+1;
				if(lcs<c[j])
				{
					lcs=c[j];
					index1=i-lcs+1;//最长公共子序列在szStr1开始的位置
					index2=j-lcs+1;//最长公共子序列在szStr2开始的位置
				}
			}
			else
				c[j]=0;
			R[i][j]=c[j];//保存公共序列关系矩阵
		}
	}
	int maxlen=( lcs<nSize? lcs:nSize);
	for(int i=0; i<maxlen; i++)
		szCommon[i]=szStr2[index2+i];
	szCommon[maxlen]='\0';
	
	delete []c;

	int mostCommon = lcs + MostCommonStrs( R, 0, index1-1, 0, index2-1) 
		+ MostCommonStrs( R, index1+lcs+1, size1-1, index2+lcs+1, size2-1);//对头部和尾部求最长公共子序列

	for(int i=0; i<size1; i++)
		delete []R[i];
	delete R;

	return mostCommon;
}

求得最多公共子串长度mostCommon之后,用两个字符串szStr1、szStr2中较长的长度max(size1, size2)减去mostCommon,结果就是俩个字符串szStr1、szStr2的距离。


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值