1. 程式人生 > >P4555 最長雙回文串

P4555 最長雙回文串

++ 容易 margin c++ 最長回文 style IT color pan

題目描述

順序和逆序讀起來完全一樣的串叫做回文串。比如acbca是回文串,而abc不是(abc的順序為abc,逆序為cba,不相同)。

輸入長度為 n的串 S ,求 S的最長雙回文子串 T ,即可將 T 分為兩部分 XY,( ∣X∣,∣Y∣≥1|X|,|Y|≥1X,Y1 )且 XY 都是回文串。

輸入輸出格式

輸入格式:

一行由小寫英文字母組成的字符串 S

輸出格式:

一行一個整數,表示最長雙回文子串的長度。

輸入輸出樣例

輸入樣例#1:
baacaabbacabb
輸出樣例#1:
12

說明

【樣例說明】

從第二個字符開始的字符串aacaabbacabb可分為aacaabbacabb兩部分,且兩者都是回文串。

對於100%的數據, 2≤∣S∣≤105

Solution:

  本題$zyys$啊!~

  很容易想到$manacher$,於是先打個板子看看,處理出以$i$為中心的最長回文半徑$p[i]$後,就斷思路了。

  我首先想到的是,在每次更新$p[i]$後,分別處理出以$i$為中心的半徑$p[i]$內,每個位置為開頭和結尾的最長回文子串長度($manacher$結束後直接枚舉斷點就可以得到答案),但是這樣強行又將復雜度拉到了$O(n^2)$。於是,開始斷線~

  後面看看巨佬們的思路,豁然**,我是真的蠢啊~

  其實,將我開始的思路修改一下即可:

  我們維護最長回文半徑$p[i]$的同時,再分別維護兩個東西,以$i$為結尾的最長回文子串的長度$ll[i]$,和以$i$為開頭的最長回文子串的長度$rr[i]$。

  那麽很顯然,因為以$i$為中心的最長回文子串長度為$p[i]-1$,所以每次更新$p[i]$後,我們只需處理出當前這個回文子串的左右邊界(中間的每個點的$ll[i],rr[i]$可以在$manacher$結束後$O(n)$處理出),則$ll[i+p[i]-1]=max(ll[i+p[i]-1],p[i]-1)$(更新以$i+p[i]-1$為結尾的最長回文長度),同理$rr[i-p[i]+1]=max(rr[i-p[i]+1],p[i]-1)$。

  跑完$manacher$後,我們$O(n)$遞推出每個$#$為斷點的$ll[i]$和$rr[i]$,其中$rr[i]$因為是$i$結尾的回文長度,所以直接順推,每往後移一位,最長回文子串長度$-2$,於是$rr[i]=max(rr[i],rr[i-2]-2)$($i-2$是上一個$#$位置),同理$ll[i]$直接逆推,類似地$ll[i]=max(ll[i],ll[i+2]-2)$。

  最後枚舉每個$#$為斷點,更新$ans$就$OK$了。

代碼:

 1 #include<bits/stdc++.h>
 2 #define For(i,a,b,c) for(int (i)=(a);(i)<=(b);(i)+=(c))
 3 #define Bor(i,a,b,c) for(int (i)=(b);(i)>=(a);(i)-=(c))
 4 #define Min(a,b) ((a)>(b)?(b):(a))
 5 #define Max(a,b) ((a)>(b)?(a):(b))
 6 using namespace std;
 7 const int N=200050;
 8 int p[N],ll[N],ans,rr[N],mx,id,cnt;
 9 char s[N],t[N];
10 int main(){
11     scanf("%s",t);
12     int len=strlen(t);
13     s[++cnt]=$,s[++cnt]=#;
14     For(i,0,len-1,1)s[++cnt]=t[i],s[++cnt]=#;
15     s[++cnt]=\0;
16     For(i,1,cnt,1){
17         if(i<mx)p[i]=Min(p[id*2-i],mx-i);
18         else p[i]=1;
19         while(s[i-p[i]]==s[i+p[i]])p[i]++;
20         if(mx<i+p[i])id=i,mx=i+p[i];
21         ll[i+p[i]-1]=Max(ll[i+p[i]-1],p[i]-1);
22         rr[i-p[i]+1]=Max(rr[i-p[i]+1],p[i]-1);
23     }
24     For(i,2,cnt,2)rr[i]=Max(rr[i],rr[i-2]-2);
25     Bor(i,2,cnt,2)ll[i]=Max(ll[i],ll[i+2]-2);
26     For(i,2,cnt,2)if(rr[i]&&ll[i])ans=Max(ans,ll[i]+rr[i]);
27     cout<<ans;
28     return 0;
29 }

P4555 最長雙回文串