Manacher
問題引入
- 求一個長度爲的字符串中迴文子串的個數?
- (或:求一個長度爲的字符串中最長的迴文子串的長度?)
- 迴文串:一個正讀和反讀相同的字符串,稱之爲迴文串。
用已有的知識
- 算法一:枚舉開頭,枚舉結尾,得到一個串,再掃一遍判斷是否爲迴文串,若是,答案加,時間複雜度。
- 算法二:枚舉開頭,枚舉結尾,得到一個串,用字符串哈希判斷其是否爲迴文串,若是,答案加,時間複雜度。
- 算法三(對下文有用):枚舉迴文串的對稱中心,分長度爲奇或偶兩種情況,分別向兩端延伸,若延伸到的最大長度爲(以奇數的情況爲例,最大擴展到和),那麼答案加上,時間複雜度。
- 枚舉迴文串的對稱中心,分長度爲奇或偶兩種情況,分別二分能延伸的長度,用字符串哈希判斷,若延伸到的最大長度爲(以奇數的情況爲例,最大擴展到),那麼答案加上,時間複雜度。
提出疑問
- 有沒有更快的方法,比如?
- 有沒有更簡潔的方法,不用哈希?
- 答案是:有!
算法介紹
- 算法名稱:Manacher’s Algorithm
- 主要思路是用已經計算出的迴文子串,利用它迴文的特性,對即將計算的迴文子串提供一定的幫助。
- 需要記錄表示已經找出的迴文子串中,右端點最靠右的迴文子串所在區間,
- 初始值分別爲。
- 以下以長度爲奇數的情況爲例,介紹算法操作流程。
- 從依次枚舉字符串的對稱中心,對於每個,考慮向兩邊擴展,擴展的最長長度爲(也就是最大擴展到)。
- 定義樸素算法爲上文“用已有的知識”中的算法三,
- 那麼我們考慮優化這個樸素算法,算法的核心是利用已有的來加速計算當前的。
- 分兩種情況:
- 一、(若長度爲偶數則)
- 直接調用樸素算法計算。
- 二、(若長度爲偶數則)
- 即說明在迴文子串內,那麼找到在這個迴文子串中對稱的點,
- 因爲對稱,所以巧妙地利用這個性質,,
- 思考:這樣是正確的嗎??
- 其實不然——
- 因爲以爲中心的迴文子串,左邊界可能超出,那麼對稱過去到了就不在大回文子串內了,不能保證也迴文,所以
- 同理,對而言,以它爲中心的實際最大回文子串可能右邊界超出,那麼是計算不到的,所以,在上式的基礎上,再次調用樸素算法。
- 至此,這個問題完美解決。
時間複雜度分析
- 算法是兩重循環,看似複雜度極限會是???
- 其實並不如此,實際上,它的複雜度十分優秀。
- 觀察調用樸素算法的條件,發現每次樸素算法的調用,只會判斷還沒有被迴文子串覆蓋過的字符,也就是說,每個字符只會被樸素算法進行一次,
- 這樣下來,時間複雜度是均攤的。