manacher

用以处理的问题形式

最简单的应用,求一个串中的最长回文串。

算法流程

Part 1 预处理

将两种不同的回文串 \(\cdots B \cdots\)\(\cdots BB \cdots\) 合并成一种情况来考虑。

在两两字符之间插入一个特殊符号,这样就无需考虑究竟是哪一种对称了。


Part 2 主体部分

这里依然是非常像 KMP 和 扩展 KMP 的,大力利用之前已有的信息,然后暴力扩展,并且拥有 \(\operatorname O(n)\) 的优秀复杂度。

利用对称的性质,记录下当前的右端点最远的回文串,然后可以利用这个串得到当前位置 \(i\) 的一个初始值,从而减少计算。

讲详细要画图,但我不想画,读者自悟不难。

int main()
{
    int i,j;
    cin>>a;n=a.length();
    for(i=0;i<n;i++)b[(i<<1)+1]=a[i],b[i<<1]='#';b[n<<1]='#';
    int lst=0;l[0]=1;
    int ans=0;
    for(n<<=1,i=1,j=1;i<=n;i++)
    {
        if((lst<<1)>=i)l[i]=min(j-i,l[(lst<<1)-i]);if(!l[i])l[i]++;
        for(;i+l[i]<=n&&i>=l[i]&&b[i+l[i]]==b[i-l[i]];l[i]++);
        // printf("%d ",l[i]);
        if(i+l[i]>j)j=i+l[i],lst=i;
        ans=max(ans,(b[i]=='#')?(l[i]-1):((l[i]>>1)-1<<1)+1);
    }
    printf("%d\n",ans);
    return 0;
}

复杂度证明

在第二个步骤中,那个 for 循环每次跑一遍的时候 必定更新最大右端点,所以复杂度可以均摊,是 \(\operatorname O(n)\) 的。

posted @ 2020-12-21 22:28  zjjws  阅读(108)  评论(0)    收藏  举报