BZOJ4384[POI2015] Trzy wieże
Description
给定一个长度为n的仅包含’B’、’C’、’S’三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符,要么有多种字符,但是没有任意两种字符出现次数相同。
Input
第一行包含一个正整数n(1<=n<=1000000),表示字符串的长度。
第二行一个长度为n的字符串。
Output
包含一行一个正整数,即最长的满足条件的子串的长度。
Sample Input
9
CBBSSBCSC
Sample Output
6
HINT
选择BSSBCS这个子串
Solution:
先分析一下题目的信息,将条件抽象一下:
设 sum[0][i],sum[1][i],sum[2][i] 分别表示B、C、S的前缀和值。
设 A[i]=sum[0][i]−sum[1][i] , B[i]=sum[1][i]−sum[2][i] , C[i]=sum[0][i]−sum[2][i] 。
于是问题转化为找到一个L和R使得 A[L]≠A[R],B[L]≠B[R],C[L]≠C[R] ,且 |n−2∗(R−L)| 最大。
这是一个三维的问题,要想办法逐渐降维,首先最好考虑的是枚举顺序,我们把
A
数组排序,依次枚举,从前面的点中寻找答案,同时每次把相同的
然后我们考虑如何用一个数据结构维护接下来的两个维度,很显然可以建一个以
然后我们发现查询的是前缀与后缀,于是使用树状数组就可以完成了。复杂度 O(nlogn) 。
还有一点需要注意:别忘了 sum[0] 也应放入数组中排序并加入BIT。
至于只有一种字符的情况,随便 O(n) 扫一下就好了。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define Max 1000001
#define M 1000005
using namespace std;
char str[M];
int B[M],C[M],sum[3][M],n;
struct Node{
int x,id;
bool operator <(const Node &a)const{
return x<a.x;
}
}A[M];
struct Tree{
int Lmx,Lse,Rmx,Rse;
Tree(){Lmx=Lse=Rmx=Rse=-1;}
}val[2][M<<1];
struct BIT{
int lowbit(int x){return x&(-x);}
void check(Tree &a,int b){
if(a.Lmx==-1)a.Lmx=b;
else{
if(b<a.Lmx){
if(C[a.Lmx]==C[b])a.Lmx=b;
else{
a.Lse=a.Lmx;
a.Lmx=b;
}
}else if((a.Lse==-1)||(b<a.Lse&&C[b]!=C[a.Lmx]))a.Lse=b;
}
if(a.Rmx==-1)a.Rmx=b;
else{
if(b>a.Rmx){
if(C[a.Rmx]==C[b])a.Rmx=b;
else{
a.Rse=a.Rmx;
a.Rmx=b;
}
}else if((a.Rse==-1)||(b>a.Rse&&C[b]!=C[a.Rmx]))a.Rse=b;
}
}
void Add(int x,int a,int p){
while(x<=M*2){
check(val[p][x],a);
x+=lowbit(x);
}
}
int Query(int x,int a,int p){
int mi=n,mx=0;
while(x){
if(val[p][x].Lmx!=-1&&C[val[p][x].Lmx]!=C[a])mi=min(mi,val[p][x].Lmx);
else if(val[p][x].Lse!=-1&&C[val[p][x].Lse]!=C[a])mi=min(mi,val[p][x].Lse);
if(val[p][x].Rmx!=-1&&C[val[p][x].Rmx]!=C[a])mx=max(mx,val[p][x].Rmx);
else if(val[p][x].Rse!=-1&&C[val[p][x].Rse]!=C[a])mx=max(mx,val[p][x].Rse);
x-=lowbit(x);
}
return max(mx-a,a-mi);
}
}BIT;
int main(){
int ans=0;
scanf("%d",&n);
scanf("%s",str+1);
for(int i=1;i<=n;){
int nxt=i;
while(nxt<=n&&str[nxt]==str[i])nxt++;
if(nxt-i>ans)ans=nxt-i;
i=nxt;
}
for(int i=1;i<=n;i++){
sum[0][i]=sum[0][i-1]+(str[i]=='B');
sum[1][i]=sum[1][i-1]+(str[i]=='C');
sum[2][i]=sum[2][i-1]+(str[i]=='S');
A[i].x=sum[0][i]-sum[1][i];
A[i].id=i;
B[i]=sum[1][i]-sum[2][i];
C[i]=sum[0][i]-sum[2][i];
}
A[0].x=0;
A[0].id=0;
sort(A,A+n+1);
for(int i=0;i<=n;){
int now=A[i].x;
int nxt=i;
while(nxt<=n&&A[nxt].x==now)nxt++;
for(int j=i;j<nxt;j++){//Calc
int id=A[j].id;
int res1=BIT.Query(B[id]+Max-1,id,0);
int res2=BIT.Query(Max*2-(B[id]+Max)-1,id,1);
if(res1>ans)ans=res1;
if(res2>ans)ans=res2;
}
for(int j=i;j<nxt;j++){//Add
int id=A[j].id;
BIT.Add(B[id]+Max,id,0);
BIT.Add(Max*2-(B[id]+Max),id,1);
}
i=nxt;
}
printf("%d\n",ans);
return 0;
}