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 數組排序,依次枚舉,從前面的點中尋找答案,同時每次把相同的 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 1000005using 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;}