Luogu P3805 【模板】manacher算法

gate

好像没发过manacher的板子,反正有现成的

简单介绍一下:

manacher是用来求回文串的算法,它最后可以得到一个数组r[i],表示以i为中心的最长回文串半径为r[i]。

但是,回文串可能会有abba、abcba两种形式,当长度为偶数时,不好确定中心点。

所以,在字符间加入特殊符号,把它变成这种形式:@a#b#b#a$ 就可以保证长度为奇数了。

首先,预处理把字符串的长度变为奇数。

然后,从1到n求出每一位的r[i]。

设现在i+r[i]最大(也就是回文区间的右端点最靠右)的i为mid,i+r[i](右端点)为right。

若现在枚举的i>right,直接两端暴力枚举即可;

若现在枚举的i<right,则当前的i被一个大区间包括了(因为mid比i先枚举,一定小于i,而right大于i)。

所以说,既然在这个区间内,那么i和关于mid对称的点j( (i+j)/2=mid,即j=2mid-i )的回文区间长度至少是相同的。

但是,这个区间也不能超过大区间的端点right,因为那后面的还没走过,就管不着了。

所以,可以得出r[i] = min(r[2*mid-i],right-i)。

在这个基础上再向外暴力枚举即可。

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;
const int maxn = 11000005;
int n,p[maxn<<1],ans;
char a[maxn],s[maxn<<1];

void change() {
    s[0] = '@';
    s[1] = '#';
    for(int i = 0; i < n; i++) {
        s[i*2+2] = a[i];
        s[i*2+3] = '#';
    }
    n = n*2+2;
    s[n] = '$';
}

void manacher() {
    int mr = 0,mid;
    for(int i = 1; i <= n; i++) {
        if(i < mr)
            p[i] = min(p[mid*2-i],mr-i);
        else p[i] = 1;
        while(s[i-p[i]] == s[i+p[i]]) p[i]++;
        if(i+p[i] > mr) {
            mr = i+p[i];
            mid = i;
        }
    }
}

int main() {
    scanf("%s",a);
    n = strlen(a);
    change();
    manacher();
    for(int i = 0; i < n; i++)
        ans = max(ans,p[i]);
    printf("%d",ans-1);
    return 0;
}
原文地址:https://www.cnblogs.com/mogeko/p/12452396.html