最长公共子序列(LCS)详解优化

问题

给定两个字符串,例如"sadstory"和"admisory",求出二者的最长公共子序列的长度;

解决思路

1.暴力法

1.设a,b长度分别为n,m;对于每个字符,有选或不选;所以共有2^(n+m)个情况;
2.在得到两个子序列后,比较两个子序列是否一样;

这个办法的时间复杂度为(O(2^(n+m) * max(n,m)));很费时间,因此考虑动态规划解法;
暴力法代码展示:

#include<bits/stdc++.h>
using namespace std;
/*
sadstory
adminsorry
*/
char s1[100],s2[100];
int dp[100][100];
int l1,l2;
int ans=0;
void DFS(int x,int y,string f1,string f2){
	if(x>l1||y>l2){
		if(f1==f2){
			int l=f1.size();
			ans=max(ans,l);
		}
		return;
	}
	for(int i=x;i<=l1;i++){
		for(int j=y;j<=l2;j++){
			DFS(i+1,j,f1+s1[i],f2);
			DFS(i,j+1,f1,f2+s2[j]);
			DFS(i+1,j+1,f1+s1[i],f2+s2[j]);
		//	DFS(i,j,f1+s1[i],f2);
		}
	}
}
int main(){
	while(true){
		gets(s1+1);
		gets(s2+1);
		l1=strlen(s1+1);
		l2=strlen(s2+1);
		string f1="",f2="";
		DFS(1,1,f1,f2);
		cout<<ans<<endl;
	}
	return 0;
}

2.动态规划

1.用dp[i][j]表示串a的前i个字符与b的前j个字符中的LCS长度;
2.当a[i]==b[j]时,有dp[i][j]=dp[i-1][j-1]+1;
3.当a[i]!=b[j]时,有dp[i][j]=max(dp[i-1][j],dp[i][j-1])

代码展示

#include<bits/stdc++.h>
using namespace std;
/*
sadstory
adminsorry
*/
char s1[100],s2[100]; 
int dp[100][100];
int main(){
	while(true){
		gets(s1+1);
		gets(s2+1);
		int l1=strlen(s1+1);
		int l2=strlen(s2+1);
		//cout<<s1+1<<" "<<s2+1<<endl;
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=l1;i++){
			for(int j=1;j<=l2;j++){
				if(s1[i]==s2[j])
					dp[i][j]=dp[i-1][j-1]+1;
				else 
					dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
		//	cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
			}			
		}
		cout<<dp[l1][l2]<<endl;
	}
	return 0;
}

总结

例题

https://blog.csdn.net/alpha_xia/article/details/114677569

拓展

我们已经求出了最长公共子串的长度,那我们如何求出公共子串的序列呢?
在这里插入图片描述

 - 根据上面的动态规划解法,我们可以知道这个方法就是存储一张表,记录该节点是否是公共子序列的一部分;
 		是:标记为1
 		不是:判断a[0~i]与b[0~j]这部分的公共子序列应该往哪个方向去找:
 			dp[i-1][j]>dp[i][j-1]  往上找
 			dp[i][j-1]>dp[i-1][j]  往左找

代码如下

void get_lcs(int l1,int l2){
	string s="";
	while(l1&&l2){
		if(flag[l1][l2]==1)
			s+=s1[l1],l1--,l2--;
		else if(flag[l1][l2]==2)
			l1--;
		else l2--;	
	}
	reverse(s.begin(),s.end());
	cout<<s<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高冷小伙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值