最长回文子串之manacher

问题描述

给出一个字符串,求出其最长回文子串。

解决方法

最直接的方法是枚举各个字符作为回文中心,向两边扩展至最大长度,时间复杂度为O(n^2),注意奇回文与偶回文要分开处理。

另一种做法是按先乘与后乘两种方式预处理出各个位置的哈希值,再二分答案,时间复杂度为O(nlogn)。

下面介绍的manacher算法可在线性时间内求出解。

由于回文分为奇回文和偶回文,为避免分类讨论,在字符间插入一个未在字符串中出现过的字符,全部转成奇回文。为避免越界,在最前端再插一个未出现过字符。例如,s=abbahop,转换后s1=##a#b#b#a#h#o#p#。

定义辅助数组p, p[i]表示以s1[i]为中心的最大回文半径,易见,p[i]-1正好是原字符串中最长回文串的长度。

manacher算法之所以快,是在求p数组时做了优化,避免了很多重复计算。设mx代表以s1[id]为中心的最长回文最右边界,即mx=id+p[id]。

假设现在求p[i],如果i<mx,那么可直接根据其关于id的对称点j设置p[i]的初值。

#include <bits/stdc++.h>
using namespace std;
char s[100005], s1[200005];
int p[200005], l, ans, mx, id;
int main() {
    scanf("%s", s);
    s1[l++] = '#'; s1[l++] = '#';
    for (int i = 0; s[i]; i++) {
        s1[l++] = s[i];
        s1[l++] = '#';
    }
    for (int i = 1; i < l; i++) {
        if (i < mx)
            p[i] = min(p[2*id-i], mx-i);
        else
            p[i] = 1;
        while (s1[i-p[i]] == s1[i+p[i]]) p[i]++;
        if (mx < i+p[i]) {
            id = i;
            mx = i+p[i];
        }
        ans = max(ans, p[i]-1);
    }
    printf("%d\n", ans);
    return 0;
}
Table of Contents