Manacher

作用

线性时间解决最长回文子串问题。

思想

Manacher充分利用了回文的性质,从而达到线性时间。
首先先加一个小优化,就是在每两个字符(包括头尾)之间加没出现的字符(如%),这样所有字符串长度就都是奇数了,方便了很多。
abcde->%a%b%c%d%e%
记录p[i]表示i能向两边推(包括i)的最大距离,如果能求出p,则答案就是max(p)-1了(以i为中点的最长回文为2*p[i]-1,但这是加过字符后的答案,把加进去的字符干掉,最长回文就是p[i]-1)。

我们假设p[1~i-1]已经求好了,现在要求p[i]:
假设当前能达到的最右边为R,对应的中点为pos,j是i的对称点。
1.当i<R时
这里写图片描述
由于L~R是回文,所以p[i]=p[j](i的最长回文和j的最长回文相同)。
这里写图片描述
这种情况是另一种:j的最长回文跳出L了。那么i的最长回文就不一定是j的最长回文了,但蓝色的肯定还是满足的。

综上所述,p[i]=min(p[2*pos-i],R-i)。
2.当i>=R时
由于后面是未知的,于是只能暴力处理了。

效率

但是这样看起来很暴力,为什么复杂度是 O(len) O ( l e n ) 的呢?因为R不会减小,每次暴力处理的时候,p[i]增大多少,就说明R增大多少,而R最多增加len次。所以复杂度是 O(len) O ( l e n ) 的。

推论

(Manchery大神告诉我的,Orz)
2018.4.10UPD:原先“就出现了新的本质不同的回文子串”的说法是错误的,很抱歉误导了读者们QAQ。
一个串本质不同的回文子串个数最多为len个,证明方法和效率差不多:每次R增加的时候,就说明可能出现了新的本质不同的回文子串。

模板

同时水掉hihoCoder1032和HDU3068。

#include
#include
#include
using namespace std;
const int maxl=1000000;

int te,p[2*maxl+5];
char s[maxl+5],now[2*maxl+5];

void readln() {scanf("%*[^\n]");getchar();}
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int reads(char *x)
{
    int len=0;char ch=getchar();if (ch==EOF) return EOF;
    s[++len]=ch;while (!Eoln(s[len])) s[++len]=getchar();s[len--]=0;
    return len;
}
int Manacher(char *s)
{
    int len=strlen(s+1);
    for (int i=1;i<=len;i++) now[2*i-1]='%',now[2*i]=s[i];
    now[len=len*2+1]='%';
    int pos=0,R=0;
    for (int i=1;i<=len;i++)
    {
        if (i2*pos-i],R-i); else p[i]=1;
        while (1<=i-p[i]&&i+p[i]<=len&&now[i-p[i]]==now[i+p[i]]) p[i]++;
        if (i+p[i]>R) {pos=i;R=i+p[i];}
    }
    int MAX=0;
    for (int i=1;i<=len;i++) MAX=max(MAX,p[i]-1);
    return MAX;
}
int main()
{
    freopen("Manacher.in","r",stdin);
    freopen("Manacher.out","w",stdout);
    scanf("%d",&te);readln();
    while (te--) reads(s),printf("%d\n",Manacher(s));
    return 0;
}

你可能感兴趣的:(Manacher,算法&数据结构总结By_ZZK)