manacher求最長回文子串演算法

2022-03-07 14:27:58 字數 1746 閱讀 6326

原文:

首先用乙個非常巧妙的方式,將所有可能的奇數/偶數長度的回文子串都轉換成了奇數長度:在每個字元的兩邊都插入乙個特殊的符號。比如 abba 變成 #a#b#b#a#, aba變成 #a#b#a#。 為了進一步減少編碼的複雜度,可以在字串的開始加入另乙個特殊字元,這樣就不用特殊處理越界問題,比如 @#a#b#a#(注意,下面的**是用c語言寫 就,由於c語言規範還要求字串末尾有乙個'\0'所以正好ok,但其他語言可能會導致越界)。

下面以字串12212321為例,經過上一步,變成了 s = "@#1#2#2#1#2#3#2#1#";

然後用乙個陣列 p[i] 來記錄以字元s[i]為中心的最長回文子串向左/右擴張的長度(包括s[i],也就是把該回文串「對折」以後的長度),比如s和p的對應關係:

s  #  1  #  2  #  2  #  1  #  2  #  3  #  2  #  1

#p 12

1252

1412

1612

121(p.s. 可以看出,p[i]-1正好是原字串中回文串的總長度)

那麼怎麼計算p[i]呢?該演算法增加兩個輔助變數(其實乙個就夠了,兩個更清晰)id和mx,其中id表示最大回文子串中心的位置,mx則為id+p[id],也就是最大回文子串的邊界。

然後可以得到乙個非常神奇的結論,這個演算法的關鍵點就在這裡了:如果mx > i,那麼p[i] >= min(p[2 * id - i], mx - i)。就是這個串卡了我非常久。實際上如果把它寫得複雜一點,理解起來會簡單很多:

//

記j = 2 * id - i,也就是說 j 是 i 關於 id 的對稱點。

if (mx - i >p[j])

p[i] =p[j];

else

/*p[j] >= mx - i

*/p[i] = mx - i; //

p[i] >= mx - i,取最小值,之後再匹配更新。

當然光看**還是不夠清晰,還是借助圖來理解比較容易。

當 mx - i > p[j] 的時候,以s[j]為中心的回文子串包含在以s[id]為中心的回文子串中,由於 i 和 j 對稱,以s[i]為中心的回文子串必然包含在以s[id]為中心的回文子串中,所以必有 p[i] = p[j],見下圖。

當 p[j] >= mx - i

的時候,以s[j]為中心的回文子串不一定完全包含於以s[id]為中心的回文子串中,但是基於對稱性可知,下圖中兩個綠框所包圍的部分是相同的,也就是

說以s[i]為中心的回文子串,其向右至少會擴張到mx的位置,也就是說 p[i] >= mx -

i。至於mx之後的部分是否對稱,就只能老老實實去匹配了。

對於 mx <= i 的情況,無法對 p[i]做更多的假設,只能p[i] = 1,然後再去匹配了。

於是**如下:

//

輸入,並處理得到字串s

int p[1000], mx = 0, id = 0

;memset(p,

0, sizeof

(p));

for (i = 1; s[i] != '

\0'; i++) }//

找出p[i]中最大的

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

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

求最長回文子串 Manacher 演算法

給定乙個字串,求出其最長回文子串。例如 1 s abcd 最長回文長度為 1 2 s ababa 最長回文長度為 5 3 s abccb 最長回文長度為 4,即 bccb。以上問題的傳統思路大概是,遍歷每乙個字元,以該字元為中點向兩邊查詢。其時間複雜度為 o n2 很不高效。1975 年,乙個叫 m...

求最長回文子串 ManaCher演算法

最長回文子串 求任一既定字串中,回文子串的最長長度 這是最容易想到的辦法。先遍歷獲得字串的所有子串,再對每個子串判斷其是不是回文串。對於長度為n的字串,子串個數為n n 1 2,加上對每個子串進行判斷,這種解法的時間複雜度為o n 3 就不寫了,下面重點介紹manacher演算法 由回文的定義可以推...