字符串的最長迴文串:Manacher’s Algorithm

題目鏈接:Longest Palindromic Substring

1. 問題描述

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

2. 各種解法複雜度

  • 暴力枚舉:O(N^2)
  • 記憶化搜索:O(N^2)
  • 動態規劃:O(N^2)
  • Manacher’s Algorithm : O(N)

3. Manacher’s Algorithm

步驟:

  1. 字符串裏相鄰兩個字符之間插入一個 # ,開頭和結尾也加上 # 。
    例如:
    Str = “abaaba”, Trans = “#a#b#a#a#b#a#”.
    注:Trans 爲 transform

  2. 聲明一個數組 P[n] : 其中 n 爲 Trans 的長度。P[index] 表示以 index 爲中心的最長迴文串長度(不包括 index 本身)。例如:

    Trans = # a # b # a # a # b # a #
        P = 0 1 0 3 0 1 6 1 0 3 0 1 0
    

    聲明變量 Cur :表示當前迴文串最長的中心的下標(current position),初值爲0。
    聲明變量 Right : 表示以Cur爲中心的迴文串最右一個字符所處位置。

  3. 建立循環,以增量 index 爲中心,擴展 index 。也就是檢查 index 兩邊是否相等。如果相等, P[index] 加1。

    在循環體中:

    • 變量 index :
      當前檢查的元素的下標。
    • 聲明變量 index_ mirror
      以Cur爲對稱軸, index 的對稱位置,index_mirror = Cur - ( index - Cur )
    • 在擴展之前檢查 Right 是否大於 index :
      如果是,那麼 P[index] 的值直接賦值爲 min(R - index, P[index_mirror]) ;否則P[index] = 0。
  4. 如果 index + P[index] > Right ,將 Cur 更新爲 index 。

  5. 找出 P[] 的最大值即是答案。

4. Q&A

  • 爲什麼要插入 # ?
    比如 abba ,以第一個 b 的下標爲 index ,如果要比較 index 的迴文,那麼它就得比較 index 和 index+1
    如果插入 #a#b#b#a# ,第一個 b 後面的 # 下標爲 index ,則比較 index - 1 和 index + 1 這樣代碼更清晰,也更好理解。此時 # 的 P[] 值就表示該位置的迴文串長度。

  • 其他

    如上圖。由於我們已經檢查出 Cur 位置的迴文串長度是 9 ,那麼 Cur 左右兩邊的 9 個字符是對稱的。如果 index_mirror 的 P[] 值小於等於 R-index 的值(即距離),那麼令 P[index] = P[index_mirror] ——對稱的性質。爲什麼要小於他們的距離?因爲如果大於他們的距離,例如圖中最左邊的 a ,它迴文串的範圍超出了 Cur 迴文串的範圍,超出 Cur 範圍的對稱性是未知的。從圖中我們可以看出,在對稱右邊的 a 顯然 P[] 值跟左邊的不相等,它是 1 。此時只能以 index 爲中心繼續比較(而不是直接令 P[index] = P[index_mirror] ,因爲對稱性質無法使用)。

    在檢測 index 的最大回文串時,如果檢測到 index 的迴文串長度最右側大於 Cur 的最右側,也就是 Right ,那麼將 Cur 更新爲 index 。因爲如果後面的元素本來可以用更新前 Cur 的對稱性,那麼更新後的 Cur 的對稱性它同樣可以用。而 Cur 的更新會使得更後面的元素可以用其對稱性。

參考鏈接:

  1. Longest Palindromic Substring Part II
  2. Manacher's ALGORITHM: O(n)時間求字符串的最長迴文子串
  3. soulmachine/leetcode -- Github
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章