Manacher算法模板

题目描述

给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.

字符串长度为n

输入输出格式

输入格式:

 

一行小写英文字符a,b,c...y,z组成的字符串S

 

输出格式:

 

一个整数表示答案

 

题解及总结

  和很多字符串算法一样,Manacher算法与其说是一种算法,还不如说是一种优化暴力,利用了回文字符串的性质从而减少了许多不必要的枚举从而达到了O(n)级别,学习之后我已经深深折服在提出者的脑洞之下。

  首先,值得优化的是回文串的长度,因为回文串本身是分奇偶的,对此我们可以在一个回文串的首尾和每个字母间都插入一个无关字符(如:井字符‘#’)这样,所有的回文串都会变成长度为奇数的回文串了。

  对于一个回文串有这样的性质:

  我们定义一个回文串关于他的回文中心对称,他的回文半径为回文串回文中心到左右端点其中一端的距离。

  如图所示对于一个回文串a(黄色左边)和b(蓝色)因为b是关于mid(b)回文(对称)那么一定存在[l(b), r(a)]与[l(a'), r(b)]也是关于mid(b)对称的,又因为a是关于mid(a)回文的回文串,那么[l(b), p]一定也关于mid(a)回文的回文串,又由上述的关于mid(b)对称可知:[p', r(b)]一定关于mid(a‘)回文。

  由此性质我们可以得到一个获得对于每个回文中心的最长回文子串的方法:

  我们记录下回文串右端点最远可以到达的点为rmax,他的回文中心为mid。

  对于任意一个点p > mid一定存在一个p' = (mid << 1)- p与之对称,那么,以p为回文中心的回文半径至少为r(p) = min( r(p'), rmax - p)。除此之外,r(p)还有可能继续向外延伸,这就可以通过枚举来确定了。

  所以,我们求整个字符串每个最长回文串的长度只需要将整个字符串扫一遍就可以完成,时间复杂度为O()。

  而且更具这个性质,我们可以得到对于一个字符串,本质不同的不可继续扩展的回文串至多有n个。

代码

#include <bits/stdc++.h>
using namespace std;

const int MAX = 11000005;
char s[MAX << 1], ch[MAX];
int p[MAX << 1];
int len, cnt;

void change()
    {
        cnt = 1;
        s[0] = s[1] = '#';
        for(int i = 0; i < len; ++ i)    s[++ cnt] = ch[i], s[++ cnt] = '#';
    }

void manacher()
    {
        int mid = 0, rmax = 0;
        for(int i = 1; i <= cnt; ++ i)
        {
            if(i < rmax)
            {
                int j = (mid << 1) - i;
                p[i] = min(p[j], rmax - i);
            }
            else    p[i] = 1;
            for(;s[i + p[i]] == s[i - p[i]]; ++ p[i]);
            if(p[i] + i > rmax)
            {
                rmax = p[i] + i ;
                mid = i;
            }
        }
    }

int main()
{
    //freopen("2.in", "r", stdin);
    //freopen("2.out", "w", stdout);
    
    scanf("%s", ch);
    len = strlen(ch);
    change();
    manacher();
    int ans = 1;
    for(int i = 0; i <= cnt; i ++) ans= max(ans, p[i]);
    printf("%d\n", ans - 1);
    return 0;
}

 

posted @ 2018-07-18 21:48  玥~endlessly~vast  阅读(88)  评论(0编辑  收藏  举报