牛客练习赛85-音乐家的曲调-(双指针+预处理)

B

题目:
就是给你一个字符串,然后让你选出三段不相交的区间,每段区间中的每个字母出现的次数不能大于m,问你这三段区间的长度总和最大是多少。

思考:
三段区间,很明显,肯定是中间先选一段,然后左边再选一段,右边再选一段。然后想到对于每个点维护这个点最右边可以到达哪,和这个点最左边能到达那。然后枚举一遍确定中间的区间,左区间就是l1,r1。有区间就是l2,r2。那么肯定是求左区间往左边的最大值,和右区间往右边的最大值。这样才不会和中间的区间右冲突。然后不知道为啥脑子抽了写了个线段树维护区间最值。1e7*4直接炸空间。然后发现,直接维护一个前缀最大值和后缀最大值就行了啊。然后就过了。还有个值得注意的就是,求这个点往左右最远的时候,有可能now到不了n就退出了,那么需要最后再补上,值就是当前点到达n点的长度。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define node_l node<<1
#define node_r node<<1|1

using namespace std;
const int mod = 1e9+7,inf = 1e9;
const int N = 1e7+5,M = 2010;

int T,n,m,k;
char s[N];
int cnt[N];
int sumr[N],suml[N];
int maxnl[N],maxnr[N];

bool check()
{
	for(int i=0;i<=26;i++) if(cnt[i]>m) return true;
	return false;
}

signed main()
{
	IOS;
	cin>>n>>m>>s+1;
	int now = 1;
	for(int r=1;r<=n;r++)
	{
		if(cnt[s[r]-'a']+1<=m) sumr[now] = r-now+1;
		else sumr[now] = r-now;
		cnt[s[r]-'a']++;
		while(check())
		{
			cnt[s[now]-'a']--;
			now++;
		}
	}
	for(int i=now;i<=n;i++) sumr[i] = n-i+1; //不到n就退出来了,代表可以走到n点都没事
	now = n;
	for(int i=0;i<=26;i++) cnt[i] = 0;
	for(int l=n;l>=1;l--)
	{
		if(cnt[s[l]-'a']+1<=m) suml[now] = now-l+1;
		else suml[now] = now-l;
		cnt[s[l]-'a']++;
		while(check())
		{
			cnt[s[now]-'a']--;
			now--;
		}
	}
	for(int i=now;i>=1;i--) suml[i] = i; //不到1就退出来了,代表走到1点都没事
	for(int i=n;i>=1;i--) maxnr[i] = max(maxnr[i],max(maxnr[i+1],sumr[i]));
	for(int i=1;i<=n;i++) maxnl[i] = max(maxnl[i],max(maxnl[i-1],suml[i]));
	int maxn = 0;
	for(int i=1;i<=n;i++)
	{
		int l1 = 1,r1 = i-1;
		int l2 = i+sumr[i],r2 = n;
		int sum = sumr[i];
		if(l1<=r1) sum += maxnl[r1];
		if(l2<=r2) sum += maxnr[l2];
		maxn = max(maxn,sum);
	}
	cout<<maxn;
	return 0;
}

总结:
多多思考呀,用最简单的方式去求解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值