1. 程式人生 > 實用技巧 >Manacher(馬拉車)演算法詳解

Manacher(馬拉車)演算法詳解

給定一個字串,求出其最長迴文子串

eg: abcba

第一步:在字串首尾,及各字元間各插入一個字元(前提這個字元未出現在串裡)。

          如 原來ma     /*       a    b      a     b     c     */

            更改ma    /*  $   #   a   #   b   #   a   #  b  #  c  #   */

第二步:設定兩個變數,mx 和 id 。mx 代表以 id 為中心的最長迴文的右邊界,也就是mx = id + mp[id];

              

        id是已知的最長的迴文串的中心,我們可以發現i關於id對稱是j

。由於i從2開始列舉過來,早就經過了j的位置,所以j位置的最長迴文串已經確定如圖所示

        **如果迴文串的子串也是迴文串,那麼這個子串關於主串中心對稱而得的子串也是一個迴文串

         如果i點跑到mx(id點回文串所確定的範圍邊界)外面去了,那麼j點無論如何縮減範圍都不可能是id迴文串的子串,就不滿足上面加粗的結論了。就一定只能從1開始慢慢試探。這就是當i>mx的時候,mp[i] = 1的原因了

        根據迴文的性質,p[i] 的值基於以下三種情況得出:

        (1)j 的迴文串有一部分在 id 的之外,如下圖:

                 

      上圖中,黑線為 id 的迴文,i 與 j 關於 id 對稱,紅線為 j 的迴文。那麼根據程式碼此時mp[i] = mx - i,即紫線。那麼p[i]還可以更大麼?答案是不可能!見下圖:

                 

     假設右側新增的紫色部分是p[i]可以增加的部分,那麼根據迴文的性質,a 等於 d ,也就是說 id 的迴文不僅僅是黑線,而是黑線 + 兩條紫線,矛盾,所以假設不成立,故mp[i] = mx - i,不可以再增加一分。

      (2)j 迴文串全部在 id 的內部,如下圖:

                

         根據程式碼,此時mp[i] =mp[j]

,那麼p[i]還可以更大麼?答案亦是不可能!見下圖:

                

        假設右側新增的紅色部分是p[i]可以增加的部分,那麼根據迴文的性質,a 等於 b ,也就是說 j 的迴文應該再加上 a 和 b矛盾,所以假設不成立,故p[i] = p[j],也不可以再增加一分。

     (3)j 迴文串左端正好與 id 的迴文串左端重合,見下圖:

                

          根據程式碼,此時p[i] = p[j]或p[i] = mx - i,並且p[i]還可以繼續增加,所以需要

          

          while (ma[i - mp[i]] == ma[i + mp[i]]) 

                  mp[i]++;

      第三步:更新id,mx;

       若新計算的迴文串右端點位置大於mx,要更新id和mx的值即:mx<mp[i]+i;更新id,mx;

      第四步:遍歷所有的mp[],算出最長迴文子串.

          性質:最長迴文長度=mp[i]-1; mp[i]個'#',mp[i]-1個字元,原長2*mp[i]-1;