Manacher(馬拉車)演算法詳解
給定一個字串,求出其最長迴文子串
eg: abcba
第一步:在字串首尾,及各字元間各插入一個字元(前提這個字元未出現在串裡)。
如 原來ma /* a b a b c */
更改ma /* $ # a # b # a # b # c # */
第二步:設定兩個變數,mx 和 id 。mx 代表以 id 為中心的最長迴文的右邊界,也就是mx = id + mp[id];
id是已知的最長的迴文串的中心,我們可以發現i關於id對稱是j
**如果迴文串的子串也是迴文串,那麼這個子串關於主串中心對稱而得的子串也是一個迴文串
如果i點跑到mx(id點回文串所確定的範圍邊界)外面去了,那麼j點無論如何縮減範圍都不可能是id迴文串的子串,就不滿足上面加粗的結論了。就一定只能從1開始慢慢試探。這就是當i>mx的時候,mp[i] = 1的原因了
根據迴文的性質,p[i] 的值基於以下三種情況得出:
(1)j 的迴文串有一部分在 id 的之外,如下圖:
上圖中,黑線為 id 的迴文,i 與 j 關於 id 對稱,紅線為 j 的迴文。那麼根據程式碼此時mp[i] = mx - i,即紫線。那麼p[i]還可以更大麼?答案是不可能!見下圖:
假設右側新增的紫色部分是p[i]可以增加的部分,那麼根據迴文的性質,a 等於 d ,也就是說 id 的迴文不僅僅是黑線,而是黑線 + 兩條紫線,矛盾,所以假設不成立,故mp[i] = mx - i,不可以再增加一分。
(2)j 迴文串全部在 id 的內部,如下圖:
根據程式碼,此時mp[i] =mp[j]
假設右側新增的紅色部分是p[i]可以增加的部分,那麼根據迴文的性質,a 等於 b ,也就是說 j 的迴文應該再加上 a 和 b ,矛盾,所以假設不成立,故p[i] = p[j],也不可以再增加一分。
(3)j 迴文串左端正好與 id 的迴文串左端重合,見下圖:
根據程式碼,此時p[i] = p[j]或p[i] = mx - i,並且p[i]還可以繼續增加,所以需要
while (ma[i - mp[i]] == ma[i + mp[i]]) mp[i]++;
第三步:更新id,mx;
若新計算的迴文串右端點位置大於mx,要更新id和mx的值即:mx<mp[i]+i;更新id,mx;
第四步:遍歷所有的mp[],算出最長迴文子串.
性質:最長迴文長度=mp[i]-1; mp[i]個'#',mp[i]-1個字元,原長2*mp[i]-1;