manacher算法 详解+模板

传送门

题目描述

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

字符串长度为n

输入输出格式

输入格式:

一行小写英文字符a,b,c...y,z组成的字符串S

输出格式:

一个整数表示答案


题解:

说道回文串,相信大家都不陌生,毕竟我们在入门的时候解决过这种问题。

最原始的做法O(n^3):

枚举每个子串所用复杂度O(n^2),然后再用O(n)的时间判断这个子串是否为回文串,如果是就更新maxn。总时间复杂度O(n^3)。具体代码不再赘述。

稍微改进了一点点的做法O(n^2):

我们枚举字符串中每一个位置O(n),算出以它为对称轴的最长回文子串的

长度O(n)。总时间复杂度O(n^2)。具体代码也不再赘述。

正解manacher算法(中文名马拉车算法)O(n):

其实马拉车算法也没什么,只不过是在n平方算法的基础上做了一个小小的记忆化优化。我们使用n平方的算法的时候,你会发现,每次重复计算回文串左右端点会十分愚蠢。于是我们可以根据前面已经算出来的答案推出当前位置的答案。

首先大家得知道一个小技巧。我们在求回文串的时候,比如ababa,对称轴是在'a'这个字符上。而abba的对称轴是在两个'b'中间,因此我们把每个字符之间加上一个无关的字符,就能保证对称轴在字符的位置上了。

例如:abba我们可以变成 #a#b#b#a#。这样不影响对称轴的求解,同时也能确保对称轴在字符的位置上。

首先我们定义len[i]是以i为对称轴所能达到的回文串的最大半径,mx为当前已经计算好的最靠右的回文串的右端点,id为这个右端点对应的对称轴。

第一种情况:i<=mx


len[i]=len[j]


从p[i]=mx-i开始往后搜索。

第二种情况:i>mx

由于此时无法定位之前的len,所以只能暴力往后搜索。

AC代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int Maxn=22000005;
char s0[Maxn],s[Maxn];
int p[Maxn],n,mx,id,ans;
inline int min(int x,int y){
    return x<y?x:y;
}
inline int max(int x,int y){
    return x>y?x:y;
}
int main(){
    scanf("%s",s0+1);
    n=strlen(s0+1);
    for(int i=0;i<=2*n;i+=2){
        s[i]='#';
        s[i+1]=s0[i/2+1];
    }
    n=n*2+1;s[0]='#';
    for(int i=1;i<n;i++){
        if(mx>i)p[i]=min(p[2*id-i],mx-i);
        else p[i]=1;
        while(i-p[i]>=0&&s[p[i]+i]==s[i-p[i]])p[i]++;
        if(p[i]+i>mx){
            mx=p[i]+i;
            id=i;
        }
        ans=max(ans,p[i]-1);
    } 
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvyanchang/article/details/80690413
今日推荐