题目:
就是给你一个字符串,然后让你选出三段不相交的区间,每段区间中的每个字母出现的次数不能大于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;
}
总结:
多多思考呀,用最简单的方式去求解。