BZOJ4384[POI2015] Trzy wieże

30 篇文章 0 订阅
23 篇文章 1 订阅

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] ,且 |n2(RL)| 最大。

这是一个三维的问题,要想办法逐渐降维,首先最好考虑的是枚举顺序,我们把 A 数组排序,依次枚举,从前面的点中寻找答案,同时每次把相同的A的点一次性加入,就可以保证 A 不同了。

然后我们考虑如何用一个数据结构维护接下来的两个维度,很显然可以建一个以B为权值的线段树,保存该 B 权值下,下标的最小值与最大值,这样就只剩一个C了。这里就可以用一点小技巧,保存的时候多记一个值。如:对于最小值,我们存一个下标的最小值,和一个下标的第二小值,且这两个值的 C 值不同。这样就可以使得无论当前查询的C值是多大,都能给出一个最优解。

然后我们发现查询的是前缀与后缀,于是使用树状数组就可以完成了。复杂度 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值