程帅霞

不断受挫,不停起身,不断追寻,不止AC~~

导航

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

      

 

posted on 2020-08-06 13:18  程帅霞  阅读(219)  评论(0编辑  收藏  举报