2019亚洲区域赛徐州网络赛 M Longest subsequence 思维

探讨了在给定字符串S中寻找一个子序列,该子序列的字典序严格大于另一给定字符串T的问题。介绍了如何通过记录每个字母位置并进行二分查找来高效求解,最终输出满足条件的子序列最长长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://nanti.jisuanke.com/t/41395
String is a very useful thing and a subsequence of the same string is equally important.

Now you have a string ss with length nn and a string tt with length mm. Find out the longest subsequence in the string ss so that the lexicographical order of this subsequence is strictly larger than tt.

Input
two integers nn, mm in the first line

(All characters are lowercase letters)

The second line is a string ss

The third line is a string tt
Output
Output an integer representing the longest length, otherwise output -1.

样例输入1 复制
9 3
aaabbbccc
abc
样例输出1 复制
6
样例输入2 复制
9 3
aaabbbccc
zzz
样例输出2 复制
-1
题目大意:给出两个字符串 S S S T T T,找到 S S S的一个子序列使得这个子序列的字典序 > T >T >T的字典序,输出满足题意的子序列的最长长度。

思路:首先要明白字符串的字典序,并不是看长度的,这里不多解释了就举个例子比如 z > a a a z>aaa z>aaa。其次要知道子序列不等于子串,不一定要连续。然后我们考虑 T T T的每一位 T [ i ] T[i] T[i],我们只有两种选择:(1)从 S S S中选择一个等于 T [ i ] T[i] T[i]的字符。(2)从 S S S中选择一个大于 T [ i ] T[i] T[i]的字符。假设选择的字符的位置是 p o s pos pos,对于第(1)种情况,我们肯定希望 p o s pos pos越小越好,对于第(2)种情况,依据字典序的定义, p o s pos pos以后的字符肯定可以全部都选上。因此我们只需要递推枚举 T [ i ] T[i] T[i],直到(1)不满足条件。关键就是如何快速的得到 p o s pos pos,我们可以用一个 26 ∗ 1 e 6 26*1e6 261e6的数组记录每一个字母出现的位置,这样每一个字母对应的位置序列都是递增的,二分查询即可。具体见代码吧。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;

const int maxn=1e6+5;

int len[30];
vector<int> vec[30];
int n,m;
char s1[maxn],s2[maxn];

int main()
{
	scanf("%d %d",&n,&m);
	scanf("%s%s",s1+1,s2+1);
	int len1=strlen(s1+1);
	int len2=strlen(s2+1);
	for(int i=1;i<=len1;i++)
		vec[s1[i]-'a'+1].push_back(i);
	for(int i=1;i<=26;i++)
		len[i]=vec[i].size();
	vector<int> :: iterator it;
	int ans=-1,cur=0,pre=0,MAX=0,dis,tmp;
	for(int i=1;i<=len2;i++)
	{
		MAX=1e9;
		for(int j=s2[i]-'a'+2;j<=26;j++)
		{
			if(!len[j])
				continue;
			it=upper_bound(vec[j].begin(),vec[j].end(),pre);
			if(it==vec[j].end())
				tmp=len1+1;
			else
				tmp=*it;
			MAX=min(MAX,tmp);
		}
		if(MAX!=1e9&&MAX!=len1+1)
			ans=max(ans,cur+len1-MAX+1);
		dis=s2[i]-'a'+1;
		if(!len[dis])
			break;
		it=upper_bound(vec[dis].begin(),vec[dis].end(),pre);
		if(it==vec[dis].end())
			break;
		pre=*it;
		++cur;
		if(i==len2&&len1>len2)//不能随便更新ans 比如aaa aaa这种情况是无解的
			ans=max(ans,cur+len1-pre);
	}
	printf("%d\n",ans);
	return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值