Manacher演算法 最長子回文串

2021-09-08 20:19:18 字數 2946 閱讀 6818

【manacher演算法】

這個演算法用來找出乙個字串中最長的回文子字串。

如果採取暴力解最長回文子字串問題,大概可以有兩種思路:1. 遍歷出所有子字串找其中最長的回文 2. 從每個字元作為中心,向兩邊擴散看是否回文。 第二種比第一種稍微高明一點,但是總體的複雜度還是o(n^2)的。

而manacher演算法可以做到o(n)時間複雜度,o(n)空間複雜度。

■  思路&描述

回文字串有乙個比較麻煩的地方,就是回文串有偶回文和奇回文兩種,分別舉例abba和abcba。這種區別可能要讓我們在程式中額外伸出乙個判斷分支來,不是很方便。所以manacher演算法的第一步就是預處理字串,將原先的字串所有字元中間再加上頭尾兩端都加上乙個特殊符號,這樣就可以把所有可能的回文串都變成了奇回文串,方便處理:

如abba變成#a#b#b#a#,abcba變成#a#b#c#b#a#。此外,一般實踐中為了保證邊界上也能統一,還會額外在頭上(理論上字串尾也可以加,但是目前尾巴上肯定是#,大不了我們遍歷的時候最後這輪以這個#字元為基礎的遍歷不去做了,這樣就可以避免尾部邊界出錯)加上乙個$或者其他有別於#的特殊符號表示字串的開始。經過這樣預處理之後的字串可以拿來被manacher演算法處理。

接下來,基本思路肯定還是要找出以每個字元為中心時,回文串最長可以做到多少。只是乙個個去遍歷太暴力了,這裡可以借鑑一下動態規劃中的一點小心思,也就是說我們能不能利用已有的資訊(當然這部分資訊是需要在之前的分析過程中手動儲存下來的)來更加方便地推出我們未知的資訊。

比如我們可以建立乙個陣列p,針對被加工過字串s,p的長度被設定為和s等長,且p中儲存的內容,是以對應原s字串中那個字元為中心,其最大回文子字串的半徑長度。由於s中所有回文串都是奇回文串,所以我們所說的半徑是指從回文串的左端開始到中心(算入中心)的長度。如#a#b#a#的半徑是4,#a#b#b#a#的半徑是5。

那麼如何基於一些現有資訊推斷新的資訊呢?考慮這樣一種情形:假如我們以i變數作為向右遍歷的下標,逐漸向右遍歷確定了乙個回文子串。這個子串的中心和半徑兩個引數都是可以明確的。不妨稱中心的下標為id,稱回文串最右端下標為mx(這也是後續編碼過程中需要使用的兩個輔助變數)。確定完id和mx之後我們繼續往右推移i。

當我們來到乙個新的i時,如果它還在mx範圍內,此時可以注意到,其實存在乙個點j,j和i關於id對稱,由於id,mx的回文特性,所以j的回文串很大可能就是i的回文串。這種可能成為確定只需要乙個條件,那就是p[j]的取值,沒有超出id,mx這個回文串的範圍。下面是盜來的圖,說明了i,j,id,mx以及mx'(mx關於id的對稱點)的布局。

那麼如果p[j] > j - mx',即j對應的回文串已經超出了mx的範圍,此時改怎麼辦?很顯然,在mx'到j的這段內容,由於id,mx的回文性,還是可以對應到i到mx這段內容的。至於mx之後的內容,只能再去乙個個字元遍歷過去看能不能符合回文。

總的來說,當i仍然小於mx時,i的取值應該可以取min(p[j],mx-i),當取p[j]的時候,p[i]就是p[j]的值。當取mx-i的時候,mx-i還只是乙個基礎值,還需要進一步處理來獲得準確值。

還沒有討論完,剛才說的都是i為了保證遞推的連續性,不論上上述哪種可能,獲得到i的回文串之後,都需要將id和mx進行更新。應該注意,id和mx並不是我們最後要求的最長回文子串的屬性,而只是當前已經遍歷過的最靠右的乙個回文子串的屬性。

而要求最長回文子串的屬性我們可以再維護乙個類似於longestinfo之類的變數,每找出乙個回文子串後判斷它是不是最長的。所有迴圈結束後去這個變數裡面取值就好了。

■  編碼實現

下面是manacher演算法的python實現:

def

longestpalindrome(s):

""":type s: str

:rtype: str

"""s = '

$#' + '

#'.join(list(s)) + '#'

#預處理

p = [0] *len(s)

longestinfo =[0,0]

i,currres,currmax = 1,0,0 #

由於s[0]是$,所以i從1開始取值。id和mx分別換了個名字currres和currmax

while i

if i > currmax: #

i已經超出mx的情況

p[i] = 1

else: #

i未超出mx,再分成兩種情況,體現在min函式中

j = 2*currres -i

p[i] = min(p[j],currmax-i)

#此時p[i]並不一定已經正確,除了決定p[i]=p[j],其他兩個分支都只是給了p[i]乙個基準值

while i-p[i]>0 and i+p[i]and s[i-p[i]] == s[i+p[i]]:

#進行乙個個字元向外擴散的回文性檢查

#注意為了防止越界訪問,還要有邊界判斷條件

p[i] += 1

#這時p[i]才得到了最終確定的值 接下來就是將其相關屬性與已有的currres和currmax比較,看是否需要更新

if i + p[i] >currmax:

#更新id和max

currres =i

currmax = i + p[i] - 1

if p[i] > longestinfo[1]:

#更新最終結果值

longestinfo =i,p[i]

i += 1center =longestinfo[0]

radius = longestinfo[1] - 1 #

注意,半徑把對稱中心本身算進去了,所以減一

return s[center-radius:center+radius+1].replace('

#','') #

直接replace去掉所有輔助字元,得到的就是原字串的結果了。

Manacher最長回文串演算法

在介紹演算法之前,首先介紹一下什麼是回文串,所謂回文串,簡單來說就是正著讀和反著讀都是一樣的字串,比如abba,noon等等,乙個字串的最長回文子串即為這個字串的子串中,是回文串的最長的那個。一.通常解決的問題 給定乙個字串,求出其最長回文子串。例如 1 s abcd 最長回文長度為 1 2 s a...

manacher演算法(最長回文串)

題目解釋 子串 小於等於原字串長度由原字串中任意個連續字元組成的子串行 回文 關於中間字元對稱的文法,即 aba 單核 cabbac 雙核 等 最長回文子串 1.尋找回文子串 2.該子串是回文子串中長度最長的。首先要知道這個演算法是用來在o n 的時間裡看乙個字串的最長回文子串的長度是什麼。其次,它...

Manacher演算法 最長回文串

若字串長度為n,則演算法的時間複雜度為o n 假設有乙個字串abaaba 先把該字串變成 a b a a b a 第乙個字元設為 防止計算的時候陣列越界 再計算p陣列,先給出p陣列的答案 i為座標,ma陣列放改變後的字串,p陣列代表以該字元為中心,向右和向左延伸p i 個,是回文串 i 0 1 2 ...