LeetCode 5 最長迴文子串--Manacher算法

題目

給定一個字符串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度爲 1000。

思路

本文一眼看上去可以用動態規劃,解法詳見下一篇博客。
動態規劃複雜度是 O(n2)O(n^2) ,看完題解後發現有更簡單的解法。
Manacher算法的複雜度可以到 O(n)O(n)
本文基本參考這篇的講解,附帶自己的理解

Manacher 算法

原理

Manacher的是從暴力搜索簡化來的。查找最長迴文串最暴力的解法就是從一個字符向兩邊擴張知道不相等就查下一個,依次遍歷完字符串就可以了。
Manacher的原理和KMP類似,也是用一個輔助數組來省略回退

本文的圖都是從這個連接裏截的,不想重新畫了

算法過程

輔助數組P
Manacher還會在原數組中均勻插入一些字符,避免在找回文中點的時候指到兩個字符中間。
上圖,P中記錄的是迴文半徑,即T字符串中一個迴文的長度的一半。很好理解我們最後就是要構建這麼一個數組P,然後找出最大值。
在這裏插入圖片描述
這裏需要幾個變量來記錄一下過程,具體過程還是看這個連接,這裏只是記錄下自己的理解。

C當前最大回文串的中心位置,R當前最大回文串的右邊界。i是要當前計算的字符。i_mirrori相對於C的鏡像位置。因爲是對稱的,P[i]可以根據P[i_mirror]計算出來。
P[i] + i,超過R的時候,更新CRiP[i]+i

每次P[i]擴張時超過RC就會跳到i。每個元素只遍歷兩次,複雜度 O(n)O(n)

代碼

func longestPalindrome(s string) string {
    var T string = preProcess(s);
    n := len(T)
    var P [2002]int;
    C := 0
    R := 0
    var i int = 0
    for i = 1; i < n - 1; i++ {
        var i_mirror = 2 * C - i
        if (R > i) {
            P[i] = R - i // 防止超出 R
            if (R - i > P[i_mirror]) {
                P[i] = P[i_mirror]
            }



        } else {
            P[i] = 0// 等於 R 的情況
        }

        // 碰到之前講的三種情況時候,需要利用中心擴展法
        for ;T[(i + 1 + P[i])] == T[(i - 1 - P[i])]; {
            P[i]++
        }

        // 判斷是否需要更新 R
        if (i + P[i] > R) {
            C = i
            R = i + P[i]
        }

    }

    // 找出 P 的最大值
    maxLen := 0
    centerIndex := 0
    for i = 1; i < n - 1; i++ {
        if (P[i] > maxLen) {
            maxLen = P[i]
            centerIndex = i
        }
    }
    start := (centerIndex - maxLen) / 2 //最開始講的求原字符串下標
    return s[start:start + maxLen]
}

func preProcess(ts string) string {
    n := len(ts)
    if (n == 0) {
        return "^$"
    }
    ret := "^"
    for i := 0; i < n; i++ {
        ret = ret + " " + string(ts[i])
    }
    ret += " $"
    return ret
}


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