1. 程式人生 > >Manacher's algorithm求最長子迴文串演算法解析

Manacher's algorithm求最長子迴文串演算法解析

Manacher’s algorithm 求最長子迴文串

用該演算法求解最長迴文子串,時間和空間複雜度都是O(n)
這裡有篇英文解釋,可供參考。演算法不太好理解,所以在理解的時候記錄下來,怕遺忘。

演算法思想

1. 準備

  1. 首先,對迴文子串做處理,每個字元之間加入一個無關字元(“#“),如abcd程式設計#a#b#c#d#,這樣做好處是,總能把迴文變成奇數個,這樣只用考慮由中心向兩邊拓展的迴文。

  2. 其次,定義需要用到的陣列或變數。

    • S:處理過後的字串,可以理解成char陣列
    • P:和s對應長度的陣列,陣列第i位記錄著S第i位為中心,除去#之後的最大的迴文的長度(也就是說求出來的長度要去除#的數量)。

    所以我們要做的就變成了將P陣列中的每個值(除去S對應下表為“#”的位置)都求解出來。

  3. 對於P陣列,我們可以先將S對應下表為“#”的位置全部置為0(因為我們不需要這些資料,也不需要計算它),同時我們總可以得到P[1] = 1。

    而接下來的步驟就是根據已經知道的值來計算後面未知的值。這個計算基於這樣顯而易見的一個規律:一個迴文串,在中心位置的左半部分邊如果是一個子迴文串,那麼對稱的右半部分也會是一個子迴文串。

    應用在長度上就是:一個迴文串,在中心位置的左半部分邊如果是一個子迴文串且長度為l,那麼對稱的右半部分也會是一個子迴文串,長度也是l

2. 演算法核心

  1. 方便理解,再定義一些變數

    • i:我們目前要求的P[i]的值的下標
    • center:包含i所在位置的最大的迴文子串的中心所在位置的下標
    • mirror:i關於center的對稱點。這個點的P[mirror]已經被求解過
  2. 那麼按照我們之前所說的規律,P[i] = P[mirror]。

        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
    T = # b # a # b # c # b # a # b # c # b # a # c # c # b #
    P = 0 1 0 3 0 1 0 7 0 ? 0 ...
    i = 9
    center = 7
    mirror = 5

    但是發現這個結論有問題,並不是都成立。

        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
    T = # b # a # b # c # b # a # b # c # b # a # c # c # b # 
    P = 0 1 0 3 0 1 0 7 0 1 0 ? ...
    i = 11
    center = 7
    mirror = 3

    這裡展示了一種不成立的情況:按照之前的假設,P[11] = P[3] = 3,但是事實上P[11] = 9,迴文串為abcbabcba.

    而不成立的時候,都是如下情況:

    • 以mirror為中心的最大回文字串 超過了 以center為中心的最大回文字串 的控制範圍
    • 換句話說,就是以mirror為中心的最大回文字串的最左端 超過了 以center為中心的最大回文字串的最左端。
    • 再精確一點,center-P[center] > mirror - P[mirror]。注意這裡的減的是P[mirror] P[center]而不是 P[mirror]/2 P[center]/2,因為字串被填充過#,而P記錄的長度是不包括#的。
  3. 基於以上討論。最終我們只要分為兩種情況

    1. center-P[center] < mirror - P[mirror],不用計算,P[i] = P[mirror]
    2. 若相反,只要按照迴文串的判斷,一個個比較字元是否相等,得到迴文最大的長度。