1. 程式人生 > >Manacher算法總結

Manacher算法總結

space font 現在 n) mage color 技術分享 namespace www

之前隊爺講的時候沒聽懂,今天考試考到了,花時間學習一下。

推薦這篇博客,講的很詳細;

題目: LGOJ[P3805]Manacher模板

求最長回文子串

1.BF思路:

O(n)枚舉對稱點,再利用回文的性質向兩邊擴展,總復雜度O(n2)

這太不優秀了QAQ

2.可以改進的地方

之前的算法主要是枚舉了重復的子串,栗子:

s="a b a b a a"

i : 1 2 3 4 5 6

在i=3時會擴展到"a b a b a"(ps:黑體為當前對稱點,以後不再說明)

而子串"a b a"在i=2時就已經被訪問過了

如何避免呢?

3.馬拉車Manacher算法:

正如上面所說,我們要避免重復的擴展,也就是要利用已有的信息

這裏我們就要充分利用回文串的性質了

下面的栗子應該可以給你一些啟發:

  串s為已經擴展完的回文串,長度為len(假設len很大),對稱點為mid(方便起見,假設len=1(mod 2))

  \(S_1,S_2,S_3...S_{mid},S_{mid+1},...,S_{len-1},S_{len}\)

  用數組\(r_i\)記錄以i為對稱點的最長回文子串半徑

  顯然\(S_i=S_{mid*2-i}(1<=i<=mid-1)\)

  假如我們現在已經知道了\(r_a(a<mid)\),那麽\(r_b(b為a的對稱點)\)可以直接推出(想一想為什麽)

這基本上就是manacher的思路了


盜一波圖:(主要是解釋程序)

①其實左邊的紅格沒啥用

技術分享圖片

②淺色的格子為i,j的回文子串

技術分享圖片

③RL就是r

技術分享圖片

在這裏雖然rj很長,但是在大於Max_Right的部分你並不知道滿不滿足i子串回文,所以ri只能到Max_Right處

上代碼

 1 #include<bits/stdc++.h>
 2 #define R register int 
 3 using namespace std;
 4 string s;
 5 char c[40000007];
 6 int ls,r[40000007];
 7 int main()
 8 {
 9     cin>>s;
10 ls=s.length(); 11 R len=0; 12 c[++len]=!,c[++len]=#;
13 for(R i=0;i<ls;i++) 14 { 15 c[++len]=s[i]; 16 c[++len]=#; 17 }//這樣處理後不用判回文串長度奇偶(c為轉換後的串) 18 R Max_Right=0,mid=0;//mid就是圖中的pos 19 for(R i=1;i<=len;i++) 20 {//Max_Right是一個重要的邊界,它的右邊是從未觸及過的點 21 if(i>Max_Right)r[i]=1;//圖①(之前從未到過i,只能老老實實地往後擴展) 22 else r[i]=min(r[mid*2-i],Max_Right-i);//圖②,③(分類討論了rj與Max_Right-mid的大小關系)以及栗子的結論 23 for(;c[i+r[i]]==c[i-r[i]]&&i+r[i]<=len&&i-r[i]>0;++r[i]);//在②的淺色格或是③的藍線兩邊兩邊繼續拓展 24 if(i+r[i]>Max_Right)//更新 25 { 26 Max_Right=r[i]+i; 27 mid=i; 28 } 29 } 30 R ans=0; 31 for(R i=1;i<=len;i++) 32 { 33 ans=max(ans,r[i]);//可以發現此時新串的半徑即為原串的長度 34 } 35 cout<<ans-1<<endl; 36 return 0; 37 }

  

  

Manacher算法總結