题意
给定一个长度为n的仅包含’B’、’C’、’S’三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符,要么有多种字符,但是没有任意两种字符出现次数相同。
1<=n<=1000000
分析
把合法的区间分成两种,一种是只出现一种字符,另一种是至少出现两种字符。
对于第一种,显然可以
O(n)
O
(
n
)
求出,那就只用考虑第二种。
由于要三种串出现的次数都不相同,转化一下可以把每个位置变成一个三维空间中的点,然后一个区间
[l,r]
[
l
,
r
]
是第二种情况当且仅当第l-1个点和第r个点的每一维都不同。
先考虑只有一维的话要怎么做。当求某个点为右端点的最长合法区间时,若它不等于第0个数,则左端点就是1,否则我们可以找到0右边第一个和0不相同的位置,然后让它作为左端点。
拓展到三维的话,我们可以强制某些位和0不一样,其余的随意。那对于每种情况,我们只用记录最多四个数。注意这四个数的三维也是不同的。
然后每次只在这些数里面找就好了。
时间复杂度
O(n)
O
(
n
)
。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=1000005;
int n,bin[5],x[N],y[N],z[N],tot[10],a[10][5];
char str[N];
bool check(int i,int j)
{
return x[i]!=x[j]&&y[i]!=y[j]&&z[i]!=z[j];
}
int main()
{
bin[0]=1;
for (int i=1;i<=3;i++) bin[i]=bin[i-1]*2;
scanf("%d",&n);
scanf("%s",str+1);
int s1=0,s2=0,s3=0,ans=0,now=0;
for (int i=1;i<=n;i++)
{
if (str[i]==str[i-1]) now++;
else now=1;
ans=std::max(ans,now);
}
for (int i=1;i<=n;i++)
{
if (str[i]=='C') s1++;
else if (str[i]=='B') s2++;
else s3++;
x[i]=s1-s2;y[i]=s2-s3;z[i]=s1-s3;
}
for (int i=1;i<=n;i++)
{
int t=(x[i]!=0?1:0)+(y[i]!=0?2:0)+(z[i]!=0?4:0);
for (int s=t;s>=0;s=!s?s-1:(s-1)&t)
{
if (tot[s]==4) continue;
bool flag=0;
for (int j=1;j<=tot[s];j++) if (!check(a[s][j],i)) {flag=1;break;}
if (!flag) a[s][++tot[s]]=i;
}
}
for (int i=1;i<=n;i++)
if (check(i,0)) ans=std::max(ans,i);
else
{
for (int j=0;j<8;j++)
for (int k=1;k<=tot[j];k++)
if (check(i,a[j][k])) ans=std::max(ans,i-a[j][k]);
}
printf("%d",ans);
return 0;
}