萌新关于LCS(最长相同子序列)题目汇总

萌新关于LCS(最长相同子序列)题目汇总

LCS也是经典DP问题啦~O(nm)的时间复杂度,滚动数组优化空间复杂度
A.Common Subsequence POJ - 1458
LCS的模板题~
tips:读字符串的时候是从下标为0开始的,不是1哦

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

char str1[1005],str2[1005];
int dp[1005][1005];
int main(){
	while(~scanf("%s %s",str1,str2)){
		memset(dp,0,sizeof(dp));
		int  len1 = strlen(str1), len2 = strlen(str2);
		for(int i = 1; i <= len1; ++i){
			for(int j = 1; j <= len2; ++j){
				if(str1[i - 1] == str2[j - 1])	dp[i][j] = dp[i - 1][j - 1] + 1;
				else	dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
			}
		}
		printf("%d\n",dp[len1][len2]);
	}
	return 0;
}

B.Palindrome POJ - 1159 / 洛谷P1435 回文字串
tips:将原字符串逆序得到另一个字符串,求它们的最长公共子序列,这样就能求得它的可以构成回文的最长字符数.(正序与倒序“公共”的部分就是回文的部分
注意POJ 1159开5005*5005的int会爆内存~要开成short
另外逆序字符串可以不用新开一个数组,也可以直接在原数组上从后往前循环。(注意algorithm里的reverse没有返回值哦

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int len;
short dp[5005][5005];
string s1,s2;

int main(){
	scanf("%d",&len);
	cin >> s1;
	s2 = s1;
	reverse(s1.begin(), s1.end());
	for(int i = 1; i <= len; ++i){
		for(int j = 1; j <= len; ++j)
			if(s1[i - 1] == s2[j - 1])	dp[i][j] = dp[i - 1][j - 1] + 1;
			else	dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
	}
	printf("%d",len - dp[len][len]);
	return 0;
}
 

其实空间可以再优化~就想01背包一样,dp数组只会用到i - 1行的数据,0-(i-2)行的数据是用不上的。我们可以用一个滚动数组来求解问题:

空间优化后的代码:

//LCS空间优化
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int len,dp[2][5005],cur,pre;
string s1,s2;

int main(){
	scanf("%d",&len);
	cin >> s1;
	s2 = s1;
	reverse(s1.begin(), s1.end());
	cur = 1, pre = 0;
	for(int i = 1; i <= len; ++i){
		for(int j = 1; j <= len; ++j)
			if(s1[i - 1] == s2[j - 1])	dp[cur][j] = dp[pre][j - 1] + 1;
			else	dp[cur][j] = max(dp[pre][j], dp[cur][j - 1]);
		swap(cur,pre);
	}
	printf("%d",len - dp[pre][len]);//注意这里必须是pre!因为做完之后会把pre和cur交换
	return 0;
}
 

C.Compromise POJ - 2250
LCS打印序列~ 只是把单词当做整体,实质就是LCS. 可以采用dfs来输出,注意只有当
ans == dp[x - 1][y - 1] + 1 && ans == dp[x - 1][y] + 1 && ans == dp[x][y - 1] + 1才算确定的找到~
p.s.本题多组输入哦.

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
 
int dp[105][105],len1,len2,cnt;
string str1[105],str2[105],s,road[105];

void dfs(int ans, int x, int y){
	if(!x || !y)	return ;
	if(ans == dp[x - 1][y - 1] + 1 && ans == dp[x - 1][y] + 1 && ans == dp[x][y - 1] + 1)	road[ans] = str1[x], dfs(ans - 1,x - 1, y - 1);
	else if(ans == dp[x - 1][y])	dfs(ans,x - 1, y);
	else if(ans == dp[x][y - 1])	dfs(ans, x, y - 1);
}
int main(){
	while(cin >> s){
		len1 = len2 = 0;
		while(s != "#"){
			str1[++len1] = s;
			cin >> s;
		}
		cin >> s;
		while(s != "#"){
			str2[++len2] = s;
			cin >> s;
		}
		memset(dp,0,sizeof(dp));
		for(int i = 1; i <= len1; ++i){
			for(int j = 1; j <= len2; ++j){
				if(str1[i] == str2[j])	dp[i][j] = dp[i - 1][j - 1] + 1;
				else	dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
			}
		}
		dfs(dp[len1][len2],len1,len2);
		cout << road[1];
		for(int i = 2; i <= dp[len1][len2]; ++i)
			cout << " " << road[i];
		putchar('\n');
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值