最长公共子序列

题目要求:
给定两个序列X和Y,基于备忘录方法,编写程序找出X和Y所有的最长公共子序列

主要思路:
本题的主要思路是先输入两个公共序列,然后采用备忘录方法来找出最长公共子序列的长度,根据最长公共子序列的长度来找出那些公共元素,把它们存到一个数组当中再输出,这样就可以解决问题了。
备忘录方法,即采用自顶向下的递归来解决问题:从目标开始,将问题划分,对子问题求解,直到边界。第一步是先对备忘录进行初始化,为了之后快速判断是否有已经填写过备忘录。然后从两个序列的最后一个数开始,如果这两个数相等,就去查询它们对应的备忘录,如果大于-1说明已经计算过,就直接输出结果,否则要查询这两个数的前一个数对应的备忘录,因为这个备忘录是由斜上方的备忘录加一得到的,这样一直查询到边界为止;如果这两个最后的数不相等,那么对应的备忘录则是由它的左边和上方的备忘录求最大值得到的,需要对这两个备忘录进行查询,如果都大于-1,说明已经计算过,就直接输出结果,否则要递归调用函数进行计算。这样就能得到两个序列的最长公共子序列的长度了。
后面我们还要找出这些构成公共子序列的元素,我采用的是回溯法,从备忘录的最后一位开始回溯,若当前位置数值与其左侧位置数值相同时,说明从左侧转移到当前位置的LCS没有延长,若当前位置数值与其上侧位置数值相同时,说明从上侧转移到当前位置的LCS没有延长,若当前位置数值与其上侧、左侧均不相同,说明此时LCS得到延长,应记录当前位置所对应的字符。因为回溯法是逆序遍历,所以我在用数组存放这些字符时是逆序存入的,输出数组是正序输出的。
在这里插入图片描述
该算法的时间复杂度O(m * n),m,n为字符串的长度。

#include <iostream>
#include <string>
#define Max 50
using namespace std;   //最长公共子字符串,递归的备忘录写法
int cc[Max][Max];

int max(int x, int y) 
{
		if (x > y) 
		{
			return x;
		} 
		else 
		{
			return y;
		}
}

int LCS_L(string s3,string s4,int len3,int len4, int cc[50][50])
{
/*	cout<<s3<<" "<<s4<<" "<<len3<<" "<<len4<<" "<<endl;
	for(int s=0;s<len3;s++)
	{
		for(int t=0;t<len4;t++)
		{
			cout<<cc[s][t]<<'\t';
		}
		cout<<'\n';
	}*/





	if (cc[len3][len4]>-1) //如果cc[len3][len4]>-1,说明不是初值,已经计算过了,直接返回备忘录里面的值             
	{
			return cc[len3][len4];
	} 
    
     // 否则将数值存进备忘录里面
		 

	if (len3==0||len4==0) 
	{
			cc[len3][len4]=0;
		}
	 else if(s3[len3]==s4[len4])//如果s3[len3]==s4[len4],此时cc[len3][len4]=cc[len3-1][len4-1]+1
	{
		if(cc[len3-1][len4-1]>-1)//此时cc[len3-1][len4-1]已经计算过了
			cc[len3][len4]=cc[len3-1][len4-1]+1;
		else
			cc[len3][len4]=LCS_L(s3,s4,len3-1,len4-1,cc)+1;//否则要重新调用函数计算
	}
	else                         //如果s3[len3]!=s4[len4],cc[len3][len4]=max(cc[len3-1][len4],cc[len3][len4-1])
	{
		if(cc[len3-1][len4]>-1&&cc[len3][len4-1]>-1)//此时cc[len3-1][len4],cc[len3][len4-1]已经计算过了
			cc[len3][len4]=max(cc[len3-1][len4],cc[len3][len4-1]);
		else
			cc[len3][len4]=max(LCS_L(s3,s4,len3-1,len4,cc),LCS_L(s3,s4,len3,len4-1,cc));//否则要重新调用函数计算
		}
	
	return cc[len3][len4];
}
              

void LCS_Print(string s3,int len3,int len4, int cc[50][50])//打印最长公共子序列 
{
	int LCS_length=cc[len3][len4];
	char lcs_print[50]=" ";//这个数组存储要打印的字符串 
	
	while(LCS_length>0)
	{
		if(cc[len3][len4]!=cc[len3-1][len4-1])
		{
			if(cc[len3-1][len4]==cc[len3][len4-1])
			{
				lcs_print[LCS_length-1]=s3[len3-1];//这个数是从斜上方加1得到的 ,并且存储的时候从后往前存储 
				len3--;len4--;
				LCS_length--;
			}
			else if(cc[len3-1][len4]>cc[len3][len4-1]) len3--;//从左边得到的数 
			else len4--;//从右边得到的数 
		}
		else
		{
			len3--;len4--;
		}
	}
	cout<<"这个最长的公共子序列是:";
	cout<<lcs_print<<" "<<endl;

}

void main()
{
	string s3,s4;
	cout<<"请输入两个序列,以空格键隔开:"<<'\n';
	cin>>s3>>s4;
	int len3 = s3.length();
    int len4 = s4.length();
	for(int i=1;i<=len3;i++)//将数组cc初始化
	{
		for(int j=1;j<=len4;j++)
		{
			cc[0][0]=0;
			cc[i][0]=0;
			cc[0][j]=0;
			cc[i][j]=-1;
		}
	}
	cout<<"两个序列的公共最长子序列的长度为:"<<LCS_L(s3,s4,len3,len4,cc)<<'\n';
	LCS_Print(s3,len3,len4,cc);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值