Manacher算法——学习笔记

什么是manachar?

Manachar算法大概就是可以O(n)求出一个串的最长回文子串。名字听起来很高端,但实际上想法和实现都很简单。
主要思想就是充分了利用回文串的对称性,使复杂度降至O(n)。

算法实现

具体如何搞呢?
1. 先对原创进行一次预处理,在头和尾以及相邻字符中间都插入一个无关字符,例如:
原串:adcdad 新串:#a#d#c#d#a#d#
这样搞就不必分类讨论回文串长度的奇偶性了,所有的回文串长度都是奇的,为算法提供了很多便利。
以下我们就可以不管原串了,直接在新串上搞事情。
2. 我们设 RL[i] 表示以i为对称中心,向两边能延伸的长度(包括i点)。
即RL[i]=Len_{以i为对称中心的最长回文串} div 2+1。
3. 看下面的栗子:

s :  # a # d # c # d # a # d #
RL:  1 2 1 2 1 4 1 2 1 4 1 2 1

显然我们只需求出RL数组,然后 max(RL[i]1) 即是最后答案。
我们从左往右依次求解,假设现在RL[1~i-1]都已得到,现在要求RL[i]:
记maxR表示之前求出的所有回文串的右端点的最大值,pos表示对应的对称中心。
如果 i<maxR
设j为i关于pos的对称点。如图
Manacher算法——学习笔记_第1张图片

由于1~pos与pos~maxL是对称的,利用RL[j]的信息我们可以初步得到RL[i]的可能值,注意如果超过maxR要扔掉。然后这不一定就是极限了,因为maxR之后的情况有所不同,还要再一步一步推一下。
如果 i>=maxR :maxR之后都是未知区域,就需要从长度为1开始推了。
算出RL[i]后更新pos和maxR。
这样就结束了。
复杂度为什么是线性呢?显然maxR是不断变大的,且每次手动推一步就意味着maxR即将变大,且推的步数就对应maxR增加的大小。所以总复杂度O(n)。

下面是代码实现:

#include
#include
#include
using namespace std;
const int maxn=250005;
char s[maxn],st[maxn];
int RL[maxn];
int Manacher(){
    int len=strlen(s+1), n=1;
    st[1]='#'; for(int i=1;i<=len;i++) st[++n]=s[i], st[++n]='#';
    int pos=0,maxR=0,res=0;
    for(int i=1;i<=n;i++){
        RL[i]=i2*pos-i],maxR-i+1):1;
        while(i-RL[i]>0&&i+RL[i]<=n&&st[i-RL[i]]==st[i+RL[i]]) RL[i]++;
        if(i+RL[i]-1>maxR) maxR=i+RL[i]-1, pos=i;
        res=max(res,RL[i]-1);
    }
    return res;
}
int main(){
    freopen("hdu3068.in","r",stdin);
    freopen("hdu3068.out","w",stdout);
    for(scanf("%s",s+1);s[1];s[1]=0,scanf("%s",s+1)) printf("%d\n",Manacher());
    return 0;
} 

你可能感兴趣的:(Manacher算法——学习笔记)