Manacher算法

来源:互联网 发布:apm地面站软件 安卓 编辑:程序博客网 时间:2024/06/06 02:49

这个算法在书上见的少,但在做题时处理字符串(尤其时回文串)时,还是一种十分优秀的算法。

Manacher算法可以在O(n)时间复杂度之内求出字符串s的最长回文子串。
定义:
p[i]:以s[i]为中心的最长回文子串,从s[i]向右延伸多长
我们先对每两个字符之间(包括开头和结尾)加入一个没出现过的字符(这里以%为例):

下标 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 原串 w a a b w s w f d 新串 % w % a % a % b % w % s % w % f % d % p[i] 1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1

这样我们就巧妙地把奇数回文串与偶数回文串统一了。
可以发现p[i]1即为原串中以s[i]为中心的最长回文子串的长度。
扫一遍就可以得到最长的回文字串。
现在关键问题就是求p[i]
显然p[1]=1
假设我们现在要求p[i],则我们已经求得了p[j](j<i),我们用mx记在i之前的回文串中,延伸至最右端的位置。id为这个回文串的中心。然后我们就有如下递推公式:
if(mx > i) p[i] = min(p[2*id-i], mx-i);
看一下这张图就很容易懂了。
这里写图片描述
然后我们就可以在O(n)的时间内求出p[i],并在O(n)的时间内求出最长回文子串了。
程序如下:

/*ID: Sunshine_cfbslLANG: C++*/#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int MAXN = 100000010;char s[MAXN*2], t[MAXN];int n, p[MAXN*2], ans;void getp() {    int i, mx = 0, id;    for(i = 1; i < n; i++) {        if(mx > i) p[i] = min(p[2*id-i], mx-i);                else p[i] = 1;        while(s[i+p[i]] == s[i-p[i]]) p[i]++;        if(p[i] + i > mx) {            mx = p[i] + i;            id = i;        }    }}int main() {    int i, l;    scanf("%s", t);    l = strlen(t);    s[++n] = '%';    for(i = 0; i < l; i++) {        s[++n] = t[i];        s[++n] = '%';    }    n++;    getp();    for(i = 1; i < n; i++) ans = max(ans, p[i]-1);    printf("%d\n", ans);    return 0;}
0 0
原创粉丝点击