回文串--Manacher算法(模板)

用途:在O(n)时间内,求出以每一个点为中心的回文串长度。

首先,有一个非常巧妙的转化。由于回文串长度有可能为奇数也有可能为偶数,说明回文中心不一定在一个字符上。所以要将字符串做如下处理:在每两个字母之间插入一个特殊字符,通常用“#”,这样所有的回文串就都变成了以一个字符为回文中心的。并且,我们需要在字符串的开头或者结尾插入另一个特殊字符,比如说“*”,防止它无休止地匹配下去。

变量声明:
mx:当前已经判断过的能成为回文串的最远长度。比如说如果字符串为bacabaaa,枚举回文中心到c,那么mx应该为5,即b的位置。
id:mx所对应的回文中心。
p[i]:以i为中心的回文串向某一边最多延伸的长度。同样是上面那个例子,p[3]=3,p[7]=2,p[1]=1.


例题:K–弗基桑
要求:数字序列,求最长回文串长度。
注意填充的时候要用数列中一定没有出现的字符进行填充。

#include <iostream>  
#include <cstring>
#include <algorithm>  
using namespace std;

const int maxn = 1e5 + 5;
int T, n;
int s[maxn], s_new[2 * maxn-100000];
int p[2 * maxn-100000];

int init()
{
    int len = n;
    s_new[0] = 1e9 + 10;
    s_new[1] = 1e9 + 11;
    int j = 2;
    for (int i = 0; i < len; i++) {
        s_new[j++] = s[i];
        s_new[j++] = 1e9 + 11;
    }
    s_new[j] = 1e9 + 12;//上面的三个数需要不相同
    return j;  // 返回 s_new 的长度
}

int manacher()
{
    int len = init();  // 取得新字符串长度并完成向 s_new 的转换
    int max_len = -1;  // 最长回文长度
    int id;
    int mx = 0;
    for (int i = 1; i < len; i++)
    {
        if (i < mx) p[i] = min(p[2 * id - i], mx - i); 
        else p[i] = 1;
        while (s_new[i - p[i]] == s_new[i + p[i]])  
            p[i]++;
        if (mx < i + p[i]){
            id = i;
            mx = i + p[i];
        }
        max_len = max(max_len, p[i] - 1);
    }
    return max_len;
}

int main()
{
    scanf("%d", &T);
    while (T--){
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%d", &s[i]);
        }
        printf("%d
", manacher());
    }
    return 0;
}
原文地址:https://www.cnblogs.com/romaLzhih/p/9489815.html