馬拉車演算法
最近在學習馬拉車演算法,簡單記錄一下心得。(如有疏漏,請指出
先看 模板題 ,要求最長迴文串的長度。
首先思考樸素演算法,顯然是 ,無法透過。而馬拉車演算法能將時間複雜度最佳化到 。
性質
- 對於一個迴文字串,必然有一個對稱中心,在對稱中心兩側的部分均全等。
- 一個迴文字串對稱之後得到的一定也是迴文字串
即 aba x aba- 但是對於奇數、偶數長度的迴文字串,這個對稱中心可能是字元,也可能在兩個字元中間。
如:ab | ba, a b a
- 但是對於奇數、偶數長度的迴文字串,這個對稱中心可能是字元,也可能在兩個字元中間。
所以考慮,在兩個字元中間都插入隔板 #,即 abba 變成 #a#b#b#a#。原長度為 n 的字串,增加 n+1 隔板,長度變成 2n+1 必然是奇數,方便統計。
思路
馬拉車演算法,即記錄一直最長迴文子串區間,對樸素演算法進行最佳化。
因為是樸素演算法兩側向外拓展,隔板對判斷迴文無影響。p[i] 陣列儲存以 str[i] 為對稱中心的迴文字串半徑長度。半徑長度中計算了隔板個數,所以得到的就是迴文字串長度。
1 | for(int i = 0, l = 0, r = -1;i<s2.size(); i++){ |
先記錄 i,列舉每個對稱中心,記錄 l 和 r 即目前最長迴文字串的左右端點。k 則是目前半徑長度。
演算法主體就是樸素演算法,向左右兩端拓展。
考慮最佳化。如果i處在一個迴文子串中,因為對稱性,可以得到與i對稱的點j的最長迴文串長度。因為i不斷增加,對稱的點的座標一定小於i即已經更新過。
由於上文推斷的性質,迴文子串可以對稱得到。注意單個字元也可以考慮成一個迴文子串。所以 k 可以從 之前的 p[j] 開始計算。
1 | (設 x 表示 [l,r] 對稱中心,j 就是 i 關於 x 的對稱點) |
然後考慮如何計算 j,因為中點座標公式,得到 ,得到 。
但是因為確定區間,且偶數情況存在。所以還應判斷 j 是否在區間內。
而如果不在區間內,很顯然應該從1開始列舉。
所以得到:
1 | int k = (i>r)? 1 : min(p[l+r-i], r-i+1); |
每次更新 l r 區間長度即可。
AC Code
1 |
|
小結
馬拉車演算法實用且容易理解,但熟練掌握還需要練習。