[學習筆記]馬拉車-Manacher
阿新 • • 發佈:2020-07-26
[學習筆記]馬拉車-Manacher
一.概念
在日常做題中,經常會遇到求迴文串的問題,而馬拉車演算法可以在 \(O(n)\) 的時間中求出一個字串的最大回文子串。順帶一提,這個演算法是音譯。然後掛一下模板連結。
二.基本思想
首先對於以下兩個串 \(aabaa\) 和 \(aabbaa\) 很顯然它們整個都是迴文的,但是如果要尋找一個所謂的“中心”,第一個串好找,第二個則說不清。然後馬拉車的一個奇妙策略是插空,即在字母與字母間插入一個“隔板”,這裡我用的'|',於是兩個串就變為了\(|a|a|b|a|a|\)和\(|a|a|b|b|a|a|\),
把插進去的隔板計算在內的話,則第一個串的”中心“是'b',第二個串的”中心“是'|',這種策略使串都成了奇數長度,於是方便找中心。
此時引入兩個變數,\(p[i]\)表示以i為中心的最大回文串的半徑,\(mid,mx\)則分別表示當前已知的最長迴文串的中心與右端點。,那麼對於一個掃描到的新點 i 。有多種情況。
case 1:它在已知的右端點內。那麼相關於中心,(有點平面鏡成像的感覺?也有可能是數軸?),i 有個對稱點 j,由對稱點公式易得(初一數學奧),\(j=2*mid-i\).那麼 j 有 一個已知的 p[j],由於\(mid*2-mx——mx\)是迴文串,理想情況下可以令 p[i]=p[j] 但是實際上有可能i+p[i] 延伸到了 mx 的右邊,此時無法根據 mx 的迴文串性質直接相等,那麼限制一下使 \(p[i]=min(p[(mid<<1)-i],mx-i+1)\)
case 2:不在右端點內,直接手動探測奧。
最後注意維護一下mx與mid就好。由於mx一直是一直增加的,i也是由1~n,所以是\(O(n)\)
三.CODE(模板)
#include<iostream> #include<cstdio> #include<algorithm> #include<fstream> using namespace std; const int MAXN=1.1*1e7; char ch[MAXN*2]; int mid,mx,tot,p[2*MAXN],ans; void read(){ char c=getchar(); ch[0]='~';ch[(tot=1)]='|'; while(c<'a'||c>'z')c=getchar(); while('a'<=c&&c<='z'){ ch[++tot]=c,ch[++tot]='|'; c=getchar(); } } int main(){ read(); for(int i=1;i<=tot;++i){ if(i<=mx)p[i]=min(p[(mid<<1)-i],mx-i+1); while(ch[i-p[i]]==ch[i+p[i]])++p[i]; if(i+p[i]>mx)mx=i+p[i]-1,mid=i; if(p[i]>ans)ans=p[i]; } cout<<ans-1; return 0; }