Manacher演算法 求最長回文子串

2022-01-11 13:43:58 字數 2933 閱讀 9674

首先,得先了解什麼是回文串。回文串就是正反讀起來就是一樣的,如「abcdcba」。o(n

3)'>o(n

2)'>我們要是直接採用暴力方法來查詢最長回文子串,時間複雜度為o(n^3),好一點的方法是列舉每乙個字元,比較較它左右距離相鄰的點是否相等,我們姑且稱之為中心檢測法,時間複雜度為o(n^2)。

當我們遇到字串為「aaaaaaaaa」,之前的演算法就會發生各個回文相互重疊的情況,會產生重複計算,然後就產生了乙個問題,能否改進?答案是能,2023年,乙個叫manacher發明了manacher algorithm演算法,俗稱馬拉車演算法,其時間複雜為o(n)。該演算法是利用回文串的特性來避免重複計算的。

在中心檢測法中,我們在遍歷的過程要考慮到回文串長度的奇偶性,比如說「abba」的長度為偶數,「abcba」的長度為奇數,這樣在尋找最長回文子串的過程要分別考奇偶的情況,是否可以統一處理了?

manacher演算法:

一)第一步是改造字串s,變為t,其改造的方法如下:

在字串s的字元之間和s的首尾都插入乙個「#」,如:s=「abba」變為t="#a#b#b#a#" 。我們會發現s的長度是4,而t的長度為9,長度變為奇數了!!那s的長度為奇數的情況時,變化後的長度還是奇數嗎?我們舉個例子,s=「abcba」,變化為t=「#a#b#c#b#a#」,t的長度為11,所以我們發現其改造的目的是將字串的長度變為奇數,這樣就可以統一的處理奇偶的情況了

二)第二步,為了改進回文相互重疊的情況,我們將改造完後的t[ i ] 處的回文半徑儲存到陣列p[ ]中,p[ i ]為新字串t的t[ i ]處的回文半徑,表示以字元t[i]為中心的最長回文字串的最端右字元到t[i]的長度,如以t[ i ]為中心的最長回文子串的為t[ l, r ],那麼p[ i ]=r-i+1。這樣最後遍歷陣列p[ ],取其中最大值即可。若p[ i ]=1表示該回文串就是t[ i  ]本身。舉乙個簡單的例子感受一下:

陣列p有一性質,p[ i ]-1就是該回文子串在原字串s中的長度,那就是p[i]-1就是該回文子串在原字串s中的長度,至於證明,首先在轉換得到的字串t中,所有的回文字串的長度都為奇數,那麼對於以t[i]為中心的最長回文字串,其長度就為2*p[i]-1,經過觀察可知,t中所有的回文子串,其中分隔符的數量一定比其他字元的數量多1,也就是有p[i]個分隔符,剩下p[i]-1個字元來自原字串,所以該回文串在原字串中的長度就為p[i]-1。

另外,由於第乙個和最後乙個字元都是#號,且也需要搜尋回文,為了防止越界,我們還需要在首尾再加上非#號字元,實際操作時我們只需給開頭加上個非#號字元,我就直接使用美元吧$,結尾不用加的原因是字串的結尾標識為'\0',等於預設加過了。這樣原問題就轉化成如何求陣列p[ ]的問題了。

三)如何求陣列p [ ]

從左往右計算陣列p[ ], mi為之前取得最大回文串的中心位置,而r是最大回文串能到達的最右端的值。

1)當 i <=r時,如何計算 p[ i ]的值了?毫無疑問的是陣列p中點 i 之前點對應的值都已經計算出來了。利用回文串的特性,我們找到點 i 關於 mi 的對稱點 j ,其值為 j= 2*mi-i 。因,點 j 、i 在以mi 為中心的最大回文串的範圍內([l ,r]),

a)那麼如果p[j]

b)如果p[ j ]>=r-i(即 j 為中心的回文串的最左端超過 l),如下圖所示。即,以點 j為中心的最大回文串的範圍已經超出了範圍[l ,r] ,這種情況,等式p[ j ]=p[ i ]還成立嗎?顯然不總是成立的!因,以點 j 為中心的回文串的最左端超過l,那麼在[ l, j ]之間的字元肯定能在( j, mi ]找到相等的,由回文串的特性可知,p[ i ] 至少等於r- i,至於是否大於r-i(圖中紅色的部分),我們還要從r+1開始一一的匹配,直達失配為止,從而更新r和對應的mi以及p[ i ]。

2)當 i > r時,如下圖。這種情況,沒法利用到回文串的特性,只能老老實實的一步步去匹配。

總體看來,manacher可以看出是中心檢測法的一種優化,我們會發現如果把這一行**刪除

if(i

else

程式也是可以執行的,但這樣是不是又變成了中心檢測法了?我認為manacher演算法的最大優點在於利用回文串對稱的性質,在處理p陣列的時候,利用指標i,j的同步性,避免了匹配失敗後的下標回退,因而將時間複雜度優化為了o(n)。

相應的**如下:

1

char

x[max];

2char s_new[max*2];3

int p[max*2];4

intinit()

516 s_new[j]='\0'

;17return

j;18}19

intmanacher()

2032

else

3336

while (s_new[i-p[i]]==s_new[i+p[i]])

3740

if(mxp[i])

4145}46

for(i=1; i)

4752}53

return

max_len;

54 }

求最長回文串 Manacher演算法

o n 效率的字串求最長回文串,感覺這個blog上寫的很詳細 有幾個要點 1 先要將字串擴充套件成2 l 1長度的,在每兩個字元之間要加上乙個用不到的字元,比如 方便處理偶數回文串。2 在擴充套件後的字串兩端要加一些特殊字元,防越界。3 ra i 陣列表示以i為中點的最長回文串,mx表示以j 1 j...

manacher演算法求最長回文串長度

manacher演算法 定義陣列p i 表示以i為中心的 包含i這個字元 回文串半徑長 將字串s從前掃到後for int i 0 i由於s是從前掃到後的,所以需要計算p i 時一定已經計算好了p 1 p i 1 假設現在掃瞄到了i k這個位置,現在需要計算p i k 定義maxlen是i k位置前所...

manacher演算法 O n 求最長回文子串

樸素的做法是求出以每個字元為中心的回文串長度,複雜度為 而manacher演算法可以在o n 時間內求解,奇數長度和偶數長度可以統一處理。根據回文串的對稱性,避免了大量不必要的比較。處理技巧 相鄰的字元之間插入乙個分隔符,串的首尾也要加,以 為例,則長度為n的字串經過處理之後變成2n 1奇數長度的字...