关于序列DP问题的集合整理(内含:最长公共子序列,最长公共子串,最长上升子序列,最长上升字串)

(写之前写一些废话:这是本人的第二篇博客,很感谢能帮到大家,祝大家学业有成!)

子串与子序列的区别(很重要的!!!)

子串  必须连续

子序列  可以连续,可以不连续

搞清楚之后,进入正题

问题一    最长公共子序列

题目 https://www.luogu.com.cn/problem/P1439

数组的定义:
f[i][j]:a[1...i]与b[1...i]的最长公共子序列的长度(以i结尾)

转移(有点难懂):
if(a[i]==b[i]) f[i][j]=f[i-1][j-1]+1(如果相等,这个最长公共子序列的长度+1)

else f[i][j]=max(f[i-1][j],f[i][j-1])(不相等,由于是子序列(可以不连续),后面的数字可能还是该子序列的,继承长度(形象点说就是:虽然我不是,但我后面的数可能是,所以我要把这个名额给他们留着))

边界(还算简单?):
f[0][0]=0(我们是从1开始的,0不用管啦) f[0][j]=f[i][0]=0(同理)

答案:

f[n][n](在例题中两个数组长度均为n,如果长度不一样用f[n][m])

完整代码:

#include<bits/stdc++.h>
using namespace std;
int dp[1001][1001],a1[2001],a2[2001],n,m;
int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++)scanf("%d",&a1[i]);
   for(int i=1;i<=n;i++)scanf("%d",&a2[i]);
   for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
     {
     	if(a1[i]==a2[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1); 
     	else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
     }
   cout<<dp[n][n];
}

当然,例题时间有点大,n^{2}的代码绝对过不了(那你讲半天在干什么),在后面的文章中,我会讲解简化的代码~

问题二  最长公共子串

题目 https://www.luogu.com.cn/problem/SP1811

数组的定义(好像有点眼熟?):
f[i][j]:a[1...i]与b[1...i]的最长公共字串的长度(以i结尾)

转移(简单多了):
if(a[i]==b[i]) f[i][j]=f[i-1][j-1]+1(如果相等,这个最长公共子串的长度+1)

else f[i][j]=0(不相等,直接为0(不愧是子串,比子序列粗暴多了))

边界(一模一样):
f[0][0]=0(我们是从1开始的,0不用管啦) f[0][j]=f[i][0]=0(同理)

答案:

在f[1...n][1...m]中找到最大值

完整代码(例题是字符串(没找到整数),整数的话改下数据类型就行了):

#include<bits/stdc++.h>
using namespace std;
string a,b;
int f[25001][25001],ans=0;
int main(){
	cin>>a>>b;
	for(int i=0;i<=a.size();i++){
		for(int j=0;j<=b.size();j++)
			f[i][j]=0;
	}
	for(int i=1;i<=a.size();i++){
		for(int j=1;j<=b.size();j++){
			if(a[i]==b[j]){
				f[i][j]=f[i-1][j-1]+1;
				ans=max(ans,f[i][j]);
			} 
			else f[i][j]=0;
		}
	}
	cout<<ans;
}

 问题三   最长上升序列

题目  https://www.luogu.com.cn/problem/B3637

数组的定义(终于是一维了):
f[i]:a[1...i]最长上升子序列的长度(以i结尾)

转移:

If(a[j]<a[i]) f[i]=max(f[j]+1,f[i])(因为可以不连续,所以讨论的是“要不要a[i]”(此处与问题四不同))

边界:
a[1...n]=1

答案:

在f[1...n]中找到最大值

完整代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int f[10001],a[10001],ans=0;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
		f[i]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i-1;j++){
			if(a[j]<a[i]){
				f[i]=max(f[j]+1,f[i]);
				ans=max(ans,f[i]);
			} 
			
		}
	}
	cout<<ans;
}

问题四  最长上升子串

题目  https://www.luogu.com.cn/problem/U454718

这个堪称几个中最简单的了

呃...我懒了,相信凭借着你极致的脑子,绝对看得懂

代码:
 

#include<bits/stdc++.h>
using namespace std;
int n,a[10001],f[10001],ans=1;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	f[1]=1;
	for(int i=1;i<=n;i++){
		if(a[i-1]<a[i]){
			f[i]=f[i-1]+1;	
			ans=max(ans,f[i]);
		} 
		else f[i]=1;
	}
	cout<<ans;
} 

啊!!!为了写这篇文章自己出了一道题,洛谷的题量还是不够

写了2小时,感谢你看完~下期见~~~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值