求最長回文子串 ManaCher演算法

2021-08-20 10:10:56 字數 2791 閱讀 2373

最長回文子串:求任一既定字串中,回文子串的最長長度

這是最容易想到的辦法。先遍歷獲得字串的所有子串,再對每個子串判斷其是不是回文串。

對於長度為n的字串,子串個數為n(n-1)/2,加上對每個子串進行判斷,這種解法的時間複雜度為o(n^3)。**就不寫了,下面重點介紹manacher演算法

由回文的定義可以推出回文串有軸對稱的特性。所以可以從字串的軸心點(可能是兩個字元中間的空位)開始向左右兩側同時進行比較,一直找到兩端不相同的字元為止,就是乙個回文子串。

但這裡存在乙個問題,字串的長度可能為奇數,也可能為偶數。軸心點的位置可能是字串中的某個字元,也可能是相鄰兩個字元之間的空位。對於從軸心點開始向兩側尋找邊界的方法來說,就需要對兩種情況都進行考慮。

2.1 解決回文子串長度奇偶性問題

難道需要每個字元位遍歷兩次?manacher告訴你那個太複雜!manacher演算法的第乙個要點就是解決回文串長度奇偶性的問題。解決的方法很巧妙,在開始遍歷之前先對母串進行預處理,在母串的首尾以及相鄰兩個字元之間插入乙個母串中沒有出現的字元。這樣處理之後,所有的回文子串都會變為長度為奇數的回文串,也就是說,我們只需要以每個字元為軸心來考慮即可。

其實我的個人理解就是,通過插入字元將原本是中間空位的軸心轉化為新字串的乙個字元

2.2 解決重複訪問的問題

基於回文串的對稱性性質,我們可以發現,有很多回文子串其實是更長的乙個回文子串的子串,而且這種子串會在更長的回文串的軸心兩側對稱出現。這種回文串顯然不是問題需要的答案,但不對其進行處理會浪費遍歷的時間。manacher演算法根據回文串的對稱性,對這種本質相同的回文串進行簡化處理。

首先,定義了三個變數:

新字串的回文半徑就是原字串的回文子串長度。那麼只要我們求出了p陣列,就能得到最長回文子串的長度。

我們從左往右地訪問字串來求p,假設當前訪問到的位置為i,即要求p[i]

i < max_right

這就意味著位置為i的字元位於乙個回文子串之中,這個回文子串的軸心是center,回文半徑為max_right - center。根據回文的對稱性,可以得出乙個公式:

其中,p[2*center -i]

如果i+p[i]>max_right則要更新max_rightcenter:

i>= max_right

此時,位置為i的字元位於已知的回文子串外部或者邊界上,我們無法快速得知其回文半徑,只能通過向兩側比較的方法。同樣需要對max_rightcenter進行更新,不再重複

對預處理過的字串從左到右按照以上步驟進行遍歷之後,我們就得到了所有位置的回文半徑陣列p。求得其中最大值就是最長回文子串的長度了。

由於最近在學習python,所以用python實現了一下manacher演算法,**如下:

#!/usr/bin/python3

# -*- coding: utf-8 -*-

debug = 1

#對字串進行插值,插入#

def insert(target):

result = "*"

for i in range(len(target)):

result += "#" + target[i]

result += "#$"

if debug:

print(result)

return result

def manacher(target):

target = insert(target)

max_right = 0 # 回文子串的最右邊界

center = 0 # 最右回文子串的軸心標記

p = [0 for i in range(len(target))] # 記錄每個軸心的回文子串半徑

for i in range(2, len(target) - 2):

if max_right > i:

p[i] = min(p[2 * center - i], max_right - i)

while target[i - p[i] - 1] == target[i + p[i] + 1]:

p[i] += 1

if i + p[i] > max_right:

max_right = i + p[i]

center = i

if debug:

print("p=", p)

return max(p)

if __name__ == '__main__':

print(manacher('abc1234321ab'))

print(manacher('asdflkjl'))

執行結果為:

*#a#b#c#1#2#3#4#3#2#1#a#b#$

('p=', [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 7, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0])

7*#a#s#d#f#l#k#j#l#$

('p=', [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0])

1

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演算法 求最長回文子串

首先,得先了解什麼是回文串。回文串就是正反讀起來就是一樣的,如 abcdcba o n 3 o n 2 我們要是直接採用暴力方法來查詢最長回文子串,時間複雜度為o n 3 好一點的方法是列舉每乙個字元,比較較它左右距離相鄰的點是否相等,我們姑且稱之為中心檢測法,時間複雜度為o n 2 當我們遇到字串...