Description
给定一个长度为n的仅包含 ′B′ 、 ′C′ 、 ′S′ 三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符,要么有多种字符,但是没有任意两种字符出现次数相同。
Input
第一行包含一个正整数n(1<=n<=1000000),表示字符串的长度。
第二行一个长度为n的字符串。
Output
包含一行一个正整数,即最长的满足条件的子串的长度。
Sample Input
9
CBBSSBCSC
Sample Output
6
Input Details
选择BSSBCS这个子串。
一开始看到此题的时候非常自然地想到USACO_2014_Open_Sliver的一道题目。若只有两种字符,那么我们可以利用前缀和的思想,将每个字符分别表示作±1,这样通过前缀和的累加,对于当前位置,只需要在前面的前缀和中找到位置最小的,与当前值不等的前缀和转移即可,时间复杂度 O(nlogn) 。
但是这题的情况不一样。它要求 [L,R] 内的元素两两不等,故不能采用前文的做法(但是前缀和的思想依旧适用)。并且由于这个原因,实际上这个问题如果单从三个元素上考虑的话就是三维的问题了。
于是按照NOIP的要求,咱不能放弃暴力啊!暴力的复杂度是 O(n2) ,显然是过不掉的。
最后毫无办法,于是只能从玄学角度入手:
首先,如果采用一般的前缀和,我们就需要判断以下几条式子:
才能得出i可以从j转移的结论,显然此时的比较很困难。最方便的比较就是将上述的前缀和分别转化成3个属性,或者说三个固定的数,使得比较的时候只需要判断i,j的三个属性是否对应不相等即可。通过对上述式子的变形,我们可以得到下述内容:
同侧的内容就是当前位置的三个属性。
在这个基础上,我们解决多维问题,首先是要对其降维。
我们考虑将所有相同的 a=sum′B′[i]−sum′C′[i] 一起查询与更新,即不让两两之间的元素产生更新,这样a不重复的问题就解决了,复杂度 O(n)→O(nlogn) 。
接下来还要再降一维才好操作。由于复杂度的问题,我们必然会想到采用数据结构去优化上述关系。所以我们考虑将
b=sum′C′[i]−sum′S′[i]
放进数据结构的下标内。那么只要在数据结构的其他节点内,找到可以更新的(
c=sum′S′[i]−sum′B[i]
不相等)pos,使得abs(pos-i)
尽可能大即可。
为了 O(1) 查询某个节点内可能的最值,我们需要在每个节点上维护最大、次大值,最小、次小值(维护次值的原因是避免最值的c与当前pos的c冲突)。
本题还是一道考验代码功底的题目,代码实现比较困难。(→_→)
当然,具体实现请看代码。我们需要分别维护[1,pos-1]和[pos+1,end]之间的情况,所以代码中分别实现了前后缀树状数组。Claris大犇的做法是将b值颠倒,使得所有的后缀会变成前缀来更新。
#include <bits/stdc++.h>
#define clear(x,val) memset(x,val,sizeof(x))
using namespace std;
static const int M=1000005;
int n,cnt[3];
char str[M];
struct node{int a,b,c;}Node[M];
int head[M<<1],nxt[M];
void add_edge(int val,int v){nxt[v]=head[val],head[val]=v;}
struct Binary_tree{
struct node{
int mx1,mx2,mi1,mi2;
node(){mx1=mx2=mi1=mi2=-1;}
void up(int val,int pos){
if(!~mi1){mi1=mx1=pos;return;}
if(mi1>pos){
if(Node[mi1].c!=val)mi2=mi1; mi1=pos;
}else if((!~mi2||mi2>pos)&&Node[mi1].c!=val)mi2=pos;
if(mx1<pos){
if(Node[mx1].c!=val)mx2=mx1; mx1=pos;
}else if((!~mx2||mx2<pos)&&Node[mx1].c!=val)mx2=pos;
}//此处的更新实现比较复杂
}bit[M<<1];
int ask(node t,int pos){
int ans=0;
if(~t.mx1&&Node[t.mx1].c!=Node[pos].c){
if(ans<t.mx1-pos)ans=t.mx1-pos;
}else if(~t.mx2&&Node[t.mx2].c!=Node[pos].c){
if(ans<t.mx2-pos)ans=t.mx2-pos;
}
if(~t.mi1&&Node[t.mi1].c!=Node[pos].c){
if(ans<pos-t.mi1)ans=pos-t.mi1;
}else if(~t.mi2&&Node[t.mi2].c!=Node[pos].c){
if(ans<pos-t.mi2)ans=pos-t.mi2;
}
return ans;
}
#define lowbit(x) x&-x
void pre_insert(int w,int pos){
while(w<M*2){
bit[w].up(Node[pos].c,pos);
w+=lowbit(w);
}
}
void suf_insert(int w,int pos){
while(w){
bit[w].up(Node[pos].c,pos);
w-=lowbit(w);
}
}
int pre_query(int w,int pos){
int ans=0;
while(w){
ans=max(ans,ask(bit[w],pos));
w-=lowbit(w);
}return ans;
}
int suf_query(int w,int pos){
int ans=0;
while(w<2*M){
ans=max(ans,ask(bit[w],pos));
w+=lowbit(w);
}return ans;
}
}pre,suf;
int main(){
scanf("%d%s",&n,str+1);
clear(head,-1),clear(nxt,-1);
Node[0]=(node){0,0,0};
add_edge(Node[0].a+M,0);
for(int i=1;i<=n;i++){
switch(str[i]){
case 'B':++cnt[0];break;
case 'C':++cnt[1];break;
case 'S':++cnt[2];break;
}
Node[i]=(node){cnt[0]-cnt[1],cnt[1]-cnt[2],cnt[2]-cnt[0]};
add_edge(Node[i].a+M,i);
}
int ans=0;
for(int i=1;i<M*2;i++){
for(int j=head[i];~j;j=nxt[j]){
int v1=pre.pre_query(Node[j].b+M-1,j),
v2=suf.suf_query(Node[j].b+M+1,j);
if(ans<v1)ans=v1;
if(ans<v2)ans=v2;
}
for(int j=head[i];~j;j=nxt[j]){
pre.pre_insert(Node[j].b+M,j);
suf.suf_insert(Node[j].b+M,j);
}
}
int pre=0;
for(int i=1;i<=n+1;i++)
if(str[pre]!=str[i]){
ans=max(ans,i-pre);
pre=i;
}
printf("%d\n",ans);
}