思路分析
分析樣例:
== TTBT BTTBTB TTT BTTB
-> TTBT TBBTTB TTT BTTB
-> TTBT TBTBBT TTT BTTB
-> TTBT TBTBBT TTT TBBT
----1-----2-----3---4--
觀察區塊 2,發現 BTTB
進行操作後與右邊的 TB
再次構成 BTTB
,我們發現在這個區塊內,可以從左向右不斷操作,我們稱這種特性為傳遞性,可以發現其具有方向。
假設區塊 2 右邊有更多的 TB
,例如 BTTBTBTBTBTB
,是否仍然存在傳遞性呢?沒錯,你可以在紙上試一下,答案肯定。
推論 1:具有 BTTB TBTB...
特徵的區塊中,可以向右不斷操作,操作具有傳遞性,方向向右。
那我們再反過來看呢?難道就不能向左不斷操作嗎?
我們在區塊 2 的左邊加上一些 BT
,可以發現可以不斷向左傳遞。
推論 1.1:BTTB TBTB...
具有向右的傳遞性;...BTBT BTTB
具有向左的傳遞性。
綜合一下:
推論 1.2:...BTBT BTTB TBTB...
具有雙向的傳遞性,在它的右方傳遞性向右,左方則向左。
考慮極限思維,...BTBT BTTB TBTB...
捨去了它的尾巴,變成 BTTB
。思考發現這也是具有雙向傳遞性的,只不過只能連續操作 1 次。
推論 1.3:BTTB
也具有雙向傳遞性。
那麼,這有什麼用呢?
再次觀察樣例(如上),可以發現透過傳遞性進行操作,區塊 3 左邊多加了一個 T
,右邊也多加了一個 T
。是的,我們可以透過區塊的傳遞性對某個連續 T
區間增加 T
。
我們對推論 1.1 再次分析,對於區間 BTTB TBTB...
,可以發現右邊的 B
變成了 T
;而對於區間 ...BTBT BTTB
,左邊的 B
變成了 T
。
由於區塊 3 在區塊 2 右邊,區塊 2 表現出向右的傳遞性,透過操作區塊 2 右邊的 B
變成 T
,由於兩個區塊相鄰,區塊 3 連續 T
的長度增加 1。同理區塊 4 表現出向左的傳遞性,鄰接於區塊 3,使得區塊 3 連續 T
在右邊又增加了 1。
推論 2:對於一個具有傳遞性的區塊,它可以使它表現出的傳遞性方向上的鄰接連續 T
區塊長度增加 1。
可以發現,當某個具有傳遞性的區塊進行操作後,它將損失其傳遞性,不再可操作,不再能給鄰接 T
區塊提供新的 T
。也就是說:
推論 3:一個具有傳遞性的區塊只能對其表現出的傳遞性方向上的鄰接連續 T
區塊貢獻一次。
綜合推論 2 和推論 3,我們可以得出推論 4:一個連續 T
區塊只能收到兩次貢獻,來自左和右的兩個方向,也就是說,每個連續 T
區間長度最多增加 2,其增加的 T
分別來源於左邊和右邊兩個方向的與其方向相反的連續性區塊。
從推論 4 的視角,我們再來看一次樣例,我們把表現出向右連續性的區塊視為 ->
,向左的視為 <-
。
TTBT -> TTT <-
可以看出連續性的方向對答案是存在影響的。
如此,我們找到每個具有連續性的區塊,區塊左邊表現出向左的連續性,並打上標記,向右也是如此,但注意要區分方向。
然後列舉一遍連續 T
區塊,如果其某個邊界處存在標記且該標記方向正確,則該方向使長度加 1,最後取所有區塊這樣執行後的長度的最大值即可。
蒟蒻已經盡力說清楚了 QAQ。
程式碼實現
#define by_wanguan
#include<iostream>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=5e5+7;
int t,n,le[N],ri[N],ans; char c[N];
int main(){
ios::sync_with_stdio(false),cin.tie(0);
cin>>t; while(t--){
cin>>n; for(int i=2;i<=n+1;i++) cin>>c[i],le[i]=ri[i]=0;
int l=1,r=l+3; ans=0; c[0]=c[1]=c[n+2]=c[n+3]='#';
le[0]=le[1]=le[n+2]=le[n+3]=ri[0]=ri[1]=ri[n+2]=ri[n+3]=0;//初始化
while(r<=n){//透過雙指標查詢有連續性的區間
l++,r++;
if(c[l]=='B'&&c[l+1]=='T'&&c[l+2]=='T'&&c[l+3]=='B'){
ri[r]++,le[l]++;//發現存在,則在兩端打上標記,le的標記方向向左,ri向右
while(c[r+1]=='T'&&c[r+2]=='B') r+=2,ri[r]++;//向左右擴充套件區塊並打上標記(找尾巴)
while(c[l-1]=='T'&&c[l-2]=='B') l-=2,le[l]++;
l=r-1,r=l+3;//l跳到r的位置,在本次操作後l和r都會++,提前減1
}
}
l=1,r=l;
while(r<=n){
l=r,l++,r++;
if(c[l]!='T') continue;//查詢連續T區間
while(c[r+1]=='T') r++;//擴充套件連續T區間
ans=max(r-l+1+ri[l-1]+le[r+1],ans);//查詢是否存在標記,注意方向
}
cout<<ans<<'\n';
}
}
有點 DP 的味道,看不懂請狠狠踢我!蒟蒻會盡量解答的。
喵。