Manacher 算法個人總結(精簡)

求解最長迴文子串必用算法:Manacher 算法。

這裏不解釋啥是迴文子串了,直接總結下算法思路。

第一步:將原字符串首尾以及字符串之間添加'#'字符,目的是原字符串迴文子串的中心點可能有兩種,奇數長度和偶數長度。例如aba和abba,正常求解需要分情況討論,所以在字符串之間加上沒出現過的字符例如'#',那麼都會變成奇數長度,不用分情況討論。#a#b#a#(7)#a#b#b#a#(9),'#'長度永遠比字符長度大1。用S指代構建的字符串

第二步,構建與S長度相同的數組P,P的每一位存儲字符串中對應該位的最大回文長度半徑。

第三步,初始化P以及最大回文的中心位置C以及最右邊位置R。R是當前最大回文子串最右邊的座標,馬拉車算法的精髓就是利用了迴文串的對稱性,所以在更新迴文長度數組P的時候,要區別當前位置i是在迴文子串內還是不在,至於兩種情況有何不同看下面。

第一步沒問題,第二步初始化過程。

例如字符串fabccbad,經過第一步得到

#f#a#b#c#c#b#a#d#.

初始化P[0]=0,P[1]=1,因爲P[0]是開頭,P[1]第一個字符最大回文肯定是它自己,長度爲1。此時中心點位置就是1,C=1。R=i+P[i],當前是R=1+1=2

此時從下標2開始循環,

首先區別當前下標i是否在R內:

if i>R:直接使用中心擴展法,也就是以i爲中心點,用前後指針往前後遍歷找最長迴文子串,然後用新的R和C來更新原來的R,C。

if i<=R:i在R內,先得到i對於當前迴文串中心點C的鏡像對應點i_mir=2*C-i。P[i]=P[i_mir]。但是這裏注意邊界情況。

第一種,i_mir如果正好在左邊界處,也就是當前字符的鏡像字符正好是字符串的頭部,那P[i]不應該等於1,因爲這個1是因爲邊界,我們人爲初始化出來的,此時P[i]仍然需要用中心擴展法來確定P[i],並且用更大的R來更新R。

第二種,i+P[i_mir]>R。也就是當前座標i雖然在迴文串內,但是i+P[i_mir],它的右邊界越過了R,此時P[i]只能等於R-i,接着在這基礎上用中心擴展求長度,此時指針應該基於半徑R-i的基礎上遍歷。

如果沒有邊界問題,P[i]=P[i_mir]。

所以對於

     0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

     # f # a # b # c # c # b # a # d #

P: 0 1 0 1 0 1 0 1 6 1 0 1 0 1 0 1 0

在c#c中間的#時,R,C更新爲14(i+P[i]),8(i),後面的字符都在R內,且沒有邊界問題,直接更新。

算法看着和中心擴展類似,但是每次我們的i超出R的時候,總會更新新的R,所以元素最多遍歷兩次,(更新新的R時一次,出現邊界問題使用中心擴展時一次)。所以算法複雜度仍然是線性O(N)。

在最後得到的P數組裏,找到元素值最大的下標i。(i-P[i])/2就是原字符串迴文子串的開始下標,P[i]就是長度。

貼個python代碼:

s='#b#b#'
def malache(s):
    P=[0]*len(s)
    P[1]=1
    C=1
    R=C+P[C]
    for i in range(2,len(s)):
        if i==R+1 or 2*C-i<=2:
            P[i]=0
            t1=i-1
            t2=i+1
            while t1>=0 and t2<len(s) and s[t1]==s[t2]:
                P[i]+=1
                t1-=1
                t2+=1
        elif i+P[2*C-i]>=R:
            P[i]=R-i
            t1=i-P[i]-1
            t2=i+P[i]+1
            while t1>=0 and t2<len(s) and s[t1]==s[t2]:
                P[i]+=1
                t1-=1
                t2+=1
        else:
            P[i]=P[2*C-i]
        if i+P[i]>R:
            R=i+P[i]
            C=i
    return P
P=malache(t)
index=P.index(max(P))
start=(index-P[index])//2
end=start+P[index]
return s[start:end]

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章