Manacher算法操作詳解

Manacher

問題引入

  • 求一個長度爲nn的字符串中迴文子串的個數?
  • (或:求一個長度爲nn的字符串中最長的迴文子串的長度?)
  • 迴文串:一個正讀和反讀相同的字符串,稱之爲迴文串。

用已有的知識

  • 算法一:枚舉開頭,枚舉結尾,得到一個串,再掃一遍判斷是否爲迴文串,若是,答案加11,時間複雜度O(n3)O(n^3)
  • 算法二:枚舉開頭,枚舉結尾,得到一個串,用字符串哈希判斷其是否爲迴文串,若是,答案加11,時間複雜度O(n2)O(n^2)
  • 算法三(對下文有用):枚舉迴文串的對稱中心,分長度爲奇或偶兩種情況,分別向兩端延伸,若延伸到的最大長度爲ss(以奇數的情況爲例,最大擴展到stis+1st_{i-s+1}sti+s1st_{i+s-1}),那麼答案加上ss,時間複雜度O(n2)O(n^2)
  • 枚舉迴文串的對稱中心,分長度爲奇或偶兩種情況,分別二分能延伸的長度,用字符串哈希判斷,若延伸到的最大長度爲ss(以奇數的情況爲例,最大擴展到stis+1..sti+s1st_{i-s+1}..st_{i+s-1}),那麼答案加上ss,時間複雜度O(nlog2n)O(n\log_2 n)

提出疑問

  • 有沒有更快的方法,比如O(n)O(n)
  • 有沒有更簡潔的方法,不用哈希?
  • 答案是:有!

算法介紹

  • 算法名稱:Manacher’s Algorithm
  • 主要思路是用已經計算出的迴文子串,利用它迴文的特性,對即將計算的迴文子串提供一定的幫助。
  • 需要記錄l,rl,r表示已經找出的迴文子串中,右端點最靠右的迴文子串所在區間,
  • 初始值分別爲0,10,-1
  • 以下以長度爲奇數的情況爲例,介紹算法操作流程。
  • 1n1-n依次枚舉字符串的對稱中心,對於每個ii,考慮向兩邊擴展,擴展的最長長度爲did_i(也就是最大擴展到stidi+1..sti+di1st_{i-d_i+1}..st_{i+d_i-1})。
  • 定義樸素算法爲上文“用已有的知識”中的算法三,
  • 那麼我們考慮優化這個樸素算法,算法的核心是利用已有的dd來加速計算當前的did_i
  • 分兩種情況:
  • 一、i>ri>r(若長度爲偶數則iri≥r
  • 直接調用樸素算法計算did_i
  • 二、iri≤r(若長度爲偶數則i<ri<r
  • 即說明ii在迴文子串stl..strst_l..st_r內,那麼找到ii在這個迴文子串中對稱的點jj
    j=l+rij=l+r-i
  • 因爲對稱,所以巧妙地利用這個性質,di=djd_i=d_j
  • 思考:這樣是正確的嗎??
  • 其實不然——
  • 因爲以爲中心jj的迴文子串,左邊界可能超出ll,那麼對稱過去到了ii就不在大回文子串stl..strst_l..st_r內了,不能保證也迴文,所以
    di=min(dj,ri+1)d_i=min(d_j,r-i+1)
  • 同理,對ii而言,以它爲中心的實際最大回文子串可能右邊界超出rr,那麼djd_j是計算不到的,所以,在上式的基礎上,再次調用樸素算法。
  • 至此,這個問題完美解決。

時間複雜度分析

  • 算法是兩重循環,看似複雜度極限會是O(n2)O(n^2)???
  • 其實並不如此,實際上,它的複雜度十分優秀。
  • 觀察調用樸素算法的條件,發現每次樸素算法的調用,只會判斷還沒有被迴文子串覆蓋過的字符,也就是說,每個字符只會被樸素算法進行一次,
  • 這樣下來,時間複雜度是均攤O(n)O(n)的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章