Manacher

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

对于一个回文串

①当长度\(len\)是偶数时,关于最中间的两个字母对称

②当长度\(len\)是奇数时,关于最中间的一个字母对称

因此,进行统一,字符串首尾和每个字符间插入一个字符,使得长度为\(2len + 1\)

那么就变成了第②种情况

定义:

p[i] 表示以i为中心的最长回文半径,则p[i] - 1就是最长回文半径

设mx表示回文串中心为id,半径为\(p[id]\)的回文串的最右边

那么在\([id - p[id], id]\)\([id,id + p[id]]\)是关于id对称的串,那么进行遍历

img

设i,j是关于id对称的,那么对于回文串在\([id - p[id],p[id] + id]\)这个区间都是对称的,满足\(p[i] = p[j]\),即\(p[i] = p[2 * id - i]\)但如果\(p[j]\)的回文串超出了最左边部分,那么p[i]的回文串半径是\(m - i\),那么\(p[i] = min(p[2 * id - i], mx - i)\)
传送门

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 11e6 + 5;
int n,p[N << 1],ans;
char a[N],s[N << 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 mx = 0, mid;
    for(int i = 1; i <= n; i++) {
        if(i < mx)
            p[i] = min(p[mid * 2 - i], mx - i);
        else p[i] = 1;
        while(s[i - p[i]] == s[i + p[i]]) p[i]++;
        if(i + p[i] > mx) {
            mx = 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;
}
posted @ 2019-10-02 09:09  Emcikem  阅读(132)  评论(0编辑  收藏  举报