Codeforces Round #741 (Div. 2) E. Rescue Niwen!

题目链接

题意:

给了一个长为 n n n的字符串,求所有的子串的最长上升子序列

范围: n < = 5000 n<=5000 n<=5000

思路:

先考虑最后一个子串所在位置,显然一定是原串的某一个后缀
然后考虑倒数第二个子串所在位置,分类讨论
情况1:为最后一个子串 [l,n]的前一位[l,n-1]
情况2:为[l-k,r] , k>0

讨论情况2
设[l-k,r]为子串s2,[l,n-1]为子串s1,[1,n]为子串s0,如果s2要小于s1那么很显然不如选s1
就是说s2一定要大于s1且要小,s0,也就是s1是s2的前缀且s2和s0的最后对应的一位要s2<s0
那么很显然选s2所对应的[l-k,r]不如选[l-k,n]
结论有了,就是我选的每一个后缀[l,n]的上一个子串要么为[l,n-1]要么为另一个后缀,即若我选了某一个段[l,r]那么我一定会把[l,r+1],[l,r+2]…,[l,n]都选上

接下来就是简单dp
设dp[i]为最后子串是以i节点的后缀,那么只需要枚举前一个后缀所在的点j转移,这里要求出i点所表示的后缀和j点所表示的后缀的最长相同长度,可以二维dp预处理

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const ll N=5e3+10;
int f[10005];
char s[N];
int dp[N][N]={0};
int main()
{
	int i,j,k,l,n,m,max1,t,r;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		scanf("%s",s+1);
		for(i=1;i<=n;i++)dp[i][n+1]=0;
		 for(k=1;k<=n-1;k++){
		 	for(r=n,l=n-k;l>=1;l--,r--){
		 		if(s[l]==s[r])
		 		dp[l][r]=dp[l+1][r+1]+1;
		 		else
		 		dp[l][r]=0;
			 }
		 }
		 max1=-1;
		 for(i=1;i<=n;i++){
		 	f[i]=n-i+1; 
		 	for(j=1;j<i;j++){
		 		k=dp[j][i];
		 		if(k==n-i+1)continue;
		 		if(s[i+k]>s[j+k])f[i]=max(f[i],f[j]+n-i-k+1);
			 }
			 max1=max(max1,f[i]);
		 }
		 printf("%d\n",max1);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值