【文文殿下】Manache算法-學習筆記
Manache算法
是一個判斷回文子串的算法,我們結合例題解釋:
題目:給定一個長度為 n 的字符串 S,求其最長回文子串 一個字符串是回文的,當且僅當反轉後的串與原串完全相等
對於這個題目,有三種主流思路:
一:Hash+二分
計算字符串的前綴hash值
枚舉中點,二分回文字串的長度
時間復雜度:$O(nlogn)$
二:回文自動機
復雜度是線性的,但是編程復雜度極高,思維難度極高。
三:Manache算法
復雜度是線性的,思維難度低,編程難度低
對於Manache算法,我們先考慮樸素做法:枚舉回文串中心,然後向兩邊擴展,這樣的復雜度是$O(N^2)$的,
但是類比KMP算法,我們在樸素算法中,沒有考慮到已經計算的部分對於之後結果的貢獻,樸素方法的突破口就在這裏了。
考慮優化:由於回文串長度分奇偶,有點麻煩,所以,我們考慮在每個字符中間插入一個‘#‘字符,來保證字符串的奇性。特別的,在字符串前兩個字符,插入\$和#,對於\$的作用是:防止數組越界,既下文代碼中的whie()函數,來確保其遇到字符串開頭立即停止(因為對於$字符,其為唯一的,不可能有字符與其匹配)。
我們引入輔助數組$len[i]$ 來表示以$i$為中心,最大回文串的半徑,顯然的,對於每一個$len[i]$,$len[i]-1$就是原來回文串的長度,我們結合一個樣例來說明:
原字符串:$ # A # B # A # A # B #
$len$數組 1 1 2 1 4 1 2 2 2 1 2 1
原來的最長回文串是$3$ 也就是$len[4]-1$ (從0開始標號)。
對吧?
接下來的問題,就是如何計算$len$數組了 , 這確實是個問題,不過我們可以通過下面的辦法解決:
考慮$len[i]$ 以及當前求出的回文右邊界$mx$ , $id$ 是對應的回文中心,如果$i<mx$ 則附上初值$min{mx-i,p[j]}$,其中,$j$是$i$關於$id$的對稱坐標,通過中點坐標公式,我們可以得出:$j=id*2-i$ 。
否則($i>=mx$)附上初值$len[i]=1$.
然後,向兩邊擴展就好了。可以結合下面的圖像理解:
帶有下劃線的部分,是已經計算得出的回文串。
代碼實現:
void Manache() { int pos=0,mx=0; for(register int i=1;i<=n;++i) { len[i]=i<mx?min(len[(pos<<1)-i],mx-i):1; while(b[i-len[i]]==b[i+len[i]]) len[i]++; if(i+len[i]>mx) mx=i+len[i],pos=i; } }
【文文殿下】Manache算法-學習筆記