bzoj 4384: [POI2015]Trzy wieże 乱搞

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33229466/article/details/81063562

题意

给定一个长度为n的仅包含’B’、’C’、’S’三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符,要么有多种字符,但是没有任意两种字符出现次数相同。
1<=n<=1000000

分析

把合法的区间分成两种,一种是只出现一种字符,另一种是至少出现两种字符。
对于第一种,显然可以 O ( n ) 求出,那就只用考虑第二种。
由于要三种串出现的次数都不相同,转化一下可以把每个位置变成一个三维空间中的点,然后一个区间 [ l , r ] 是第二种情况当且仅当第l-1个点和第r个点的每一维都不同。
先考虑只有一维的话要怎么做。当求某个点为右端点的最长合法区间时,若它不等于第0个数,则左端点就是1,否则我们可以找到0右边第一个和0不相同的位置,然后让它作为左端点。
拓展到三维的话,我们可以强制某些位和0不一样,其余的随意。那对于每种情况,我们只用记录最多四个数。注意这四个数的三维也是不同的。
然后每次只在这些数里面找就好了。
时间复杂度 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;
}

猜你喜欢

转载自blog.csdn.net/qq_33229466/article/details/81063562