Manacher算法

我们知道优化非启发性算法的方法常常包括使用定理、利用计算机的架构特点、使用恰当的数据结构以及动态规划等。而动态规划的核心理念就是减少重复计算。
对于回文串问题的Manacher算法来说,我们要重用0..(i-1)的结果,那怎么重用呢?假设ij关于p(i > p > j)对称,那么R[i]便可通过R[j]求得。
计算最长回文串的暴力算法是O(n^2),而马拉车算法能够在O(n)时间内解决问题。

Manacher算法

我们使用R[i]表示i位置处的回文半径,即字符串abab字符的回文半径为2。我们现在使用i从左开始遍历字符串求R[i],为了能够DP,我们假设有一个合适的p<i,我们把i关于p做对称得到j。注意到R[j]R[p]是已求得的,待求R[i]
此时字符串坐标轴上出现了五个刻度:pjij+R[j]p+R[p],我们需要讨论他们的位置关系。由假设有j<p<i,可是j+R[j]p+R[p]关系不好确定,为了方便讨论,我们可以假设这个“合适的”p满足p+R[p]>j+R[j]。这是因为在后面的讨论中我们发现要使得p+R[p]尽可能大,假如p+R[p]<j+R[j],也就是以j为中心的回文串的右边界比以p为中心的右边界还要靠右,那么我们与其取p,不如直接取j,而且j也是在p前面被遍历。
现在我们可以得到三组位置关系

j<j+R[j]<p+R[p]j<p<p+R[p]p<i

可以看出只有j+R[j]pip+R[p]这两个位置关系尚未确定。在下面的讨论中,我们发现只有这两个都满足一定关系时,才能够重用结果。

p+R[p]i的位置关系

ip+R[p]时,i已经在p为中心的回文串外面了,以i中心的回文串看来是借不了这个p的东风了,我们需要重新找一个“回文边界”更靠右的p。所以我们直接维护一个p,使得p+R[p]始终是最大的,如果这最大还不够大,即这边界最靠右的p都包不住i,那i便只能自力更生暴力一波了。

j+R[j]p的位置关系

根据上节讨论,i>p+R[p]时只能暴力,并且j有可能越界到小于0,所以只有ip+R[p]是才会执行这一步,我们认为现在j<p<ip+R[p]
j+R[j]p的位置关系决定了i+R[j]p+R[p]的位置关系。

  1. 在内部
    j+R[j]p,则i+R[j]p+R[p],这就相当于以i至少有一个以R[j]为半径的回文串,并且在以p为中心的回文串的内部。预示我们只需要从R[j]+1开始检测i位置是否具有更大半径即可。
  2. 在外部
    j+R[j]>p,则i+R[j]>p+R[p],这就相当于j中心回文串的边界在p中心的回文串的边界之外,所以我们只能重用j中心回文串在p内的对称部分,其半径为2×R[j]R[p]+jp,然后从这个半径向外开始暴力

我们发现这两个情况可以直接合并取一个半径的最小值min(R[j],2×R[j]R[p]+jp)

偶数长度的回文串

这个时候就没办法定义“半径”这个概念了,有一个巧妙的方法是将其变为奇数长度的回文串,也就是在头尾以及每个字符中间加入一个特殊字符。

模板代码