BZOJ4384[POI2015] Trzy wieże

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章