Manacher---p的求法不懂

/*
manacher是字符串最长回文子串算法,O(n)
对于一个字符串aaabba,最长回文子串是abba
对于一个回文子串一定有一个对称轴,如果想O(n)求出最长回文子串,我们只需枚举每个对称轴,然后O(1)递推即可
所以manacher的一个操作就是在所以中间的位置插入一个'#'之类的无用字符,这样我们可以忽略对称轴在两个字符中间的情况
而只考虑对称轴落在字符上的情况(上面的对称轴在ab之间,如果是按照对称轴为一个字符,比较好找)

*/

求回文串

暴力

找出所有的子串,遍历每个子串判断是否为回文串,时间复杂度O(\(n^{3}\))

优化

回文串是对称的,所以,枚举每个位置,找到这个位置能扩展到的最大回文串
时间复杂度O(\(n^{2}\))

manacher

对于优化后的算法分析发现
对于长度为奇数的回文和长度为偶数的回文,对称轴是不一样的,奇数是一个字符,偶数是两个字符之间,所以需要分类讨论
有些子串会被访问多次

处理字符串长度的问题

在原来的字符串里,插入一个相同的无关字,长度变成了2*len+1,都变成了奇数
而最前面需要加一个另外的字符,防止数组越界???
找最长回文串
定义回文半径,把一个回文串中最左或最右位置的字符到其对称轴的距离称为回文半径

char : # a # b # c # b # a #
p[i] : 1 2 1 2 1 6 1 2 1 2 1
p[i] - 1 : 0 1 0 1 0 5 0 1 0 1 0
i : 1 2 3 4 5 6 7 8 9 10 11

所以,答案就是最大的p[i]-1
那么问题就是如何求p数组了

利用回文串的对称性扩展回文串,p[i]不再直接赋值为1,而是根据之前求出的p[j],0 我们用mx表示所有字符产生的最大回文子串的最大右边界,
id表示产生这个最大右边界的对称轴的位置。

情况1:i 情况2:i>=mx

模板

传送门

#include 
#include 
#include 
#include 
using namespace std;
const int maxn=11e6+5;
char str[maxn];
int Mp[maxn<<1];
char Ma[maxn<<1];//存放包含无用字符#的字符串
int len;
void Manacher(char str[],int len){
    int l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0;ii?min(Mp[2*id-i],mx-i):1;
        while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;
        if(i+Mp[i]>mx){
            mx=i+Mp[i];
            id=i;
        }
    }
}
int main(){
    while(~scanf("%s",str)){
        len=strlen(str);
        Manacher(str,len);
        int ans=0;
        for(int i=0;i<2*len+2;i++){
            ans=max(ans,Mp[i]-1);
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(Manacher---p的求法不懂)