{算法}一发Manacher并不难

Manacher,是一种求最长回文子串的算法。
它强大的速度总令人不明觉厉,给这个算法披上迷人的外衣。
然而,Manacher所运用的,几乎能算作常识。

一般我们求最长回文字串,分别考虑长度为奇数或偶数,分类讨论,再枚举中心点,最枚举半径,使用 O(n2) 的时间复杂度,这是再暴力不够了。
那么,我们应当如何优化这个暴力呢?
首先,我们可以在每两个字符中插入一个神♂秘符号,例如#。
这样就不必考虑长度的奇偶性了;
为了叙述方便,设f[i]为以i为中心,最长回文字串的半径。
我们来看下面这种情况:

Tips:有些F[i]写错了呵呵呵…这是陷阱绝不是博主懒!

此时,求对应a的f[12]。假设我们已知之前的f[1..11],可以不通过枚举得出f[12]吗?

我们知道,回文串是具有对称性的。
所以,f[12]对应f[8],f[12]=f[8],不是吗?
所以,f[18]对应f[2],f[18]=f[2],不是吗?
还真就不是。
f[18]=5;f[2]=1;
事实上,我们只能说f[18]>=f[2],因为从19开始,就不再具有对称性了,就有可能出现一些”特殊情况”。至于第一句话嘛…刚好没出车祸罢了。
而Manacher的快,不过是利用了对称性。
每当我们需要求F[i]前,都维护一个中心mid,使以mid为中心的回文串包含st[i]。那么f[i]>=f[mid-(i-mid)],即它的对称点。
仅仅如此吗?还有一个特殊情况。

Tips:机智的博主保留了作图痕迹。
不管其他,一个问题:F[22]=F[4]?
显然”特殊情况”。
尽管我们不能得出F[22]>=F[4],但是可以得出F[22]>=(13+f[13])-22
请读者感性的认识一下,确实难以解释。
Tips:这次真不是博主懒,真的不好解释,如果真的需要,可以去找那些画了5,6张图的人,有图会方便些

说了这么多,还是放个模板好理解。

    int mid=0;//mid即中心点
    fo(i,1,n)//当前求f[i]
    {
        if(mid+f[mid]>=i) f[i]=min(mid+f[mid]-i,f[mid-(i-mid)]);//考虑两种特殊情况,保守估初值
        while(st[i+f[i]+1] && st[i+f[i]+1]==st[i-f[i]-1]) f[i]++;//往外扩张
        if(i+f[i]>mid+f[mid]) mid=i;//使以mid为中心的回文串包含st[i]
    }

很好,非常好,收工(笑)。
Reference

Manacher算法 By 无名翻译员

你可能感兴趣的:({算法}一发Manacher并不难)