manacher 算法

manacher 算法

O(n) 求有关回文的字符串算法。

定义 [l,r] 表示字符串下标 lr 形成的子串。

流程

  1. 将原字符串首、尾、中间分别插入奇怪字符(如 #,目的是计算中心点在两个字符之间的回文串),再在最前端插入另一奇怪字符串(如 @,目的是防止数组越界),eg. abcb 变为 @#a#b#c#b#
  2. 对每个位置记录 pi 表示在当前字符串以 i 为回文中心的最大回文串为 [ipi+1,i+pi1]
  3. 考虑从前往后做,边做边记录两个值,分别为 id,maxright 分别表示当前所有回文串中,右端点最大的回文串对应的回文中心以及右端点。显然这个可以边做边维护。
  4. 考虑如何求 pi,如果说 maxright>i,那么 i 关于 id 对称的 j(i+j=2×idj=2×idi) 的信息我们是可以用到的(若 i+pj1 超出 maxright,则超出的那个部分不可取)。具体来说,如果 maxright>ipi=min(maxrighti+1,pj),否则 pi=0
  5. 不断扩大 pi,直到求出 pi

时间复杂度证明

  1. pi=maxrighti+1,则 maxright 随着 pi 增大而增大。
  2. pi=pj,则 pi 不会再继续扩展。
  3. pi=0,则 maxright 随着 pi 增大而增大。

故,maxright 均会随着 pi 增大而增大,又考虑到 maxright 最多增大 n 次,故时间复杂度为 O(n)

回文串大小

根据 pi 的定义,其实对应到原串上,以 i 为对称中心的最大的回文串长度为 pi1(可以分情况讨论,这里不展开)。

题目链接

#include <bits/stdc++.h>
const int N = 1.1e7 + 10;
int len, lenr;
char s[N], r[N * 2];
int p[N * 2], ans;
inline void manacher()
{
    int id = 0, maxright = 0;
    for (int i = 1; i <= lenr; ++ i)
    {
        if (maxright > i)
            p[i] = std::min(maxright - i + 1, p[id * 2 - i]);
        else p[i] = 0;
        while (r[i + p[i]] == r[i - p[i]])
            p[i] ++;
        if (i + p[i] - 1 > maxright)
        {
            id = i;
            maxright = i + p[i] - 1;
        }
        ans = std::max(ans, (p[i] - 1));
    }
}

int main()
{
    scanf("%s", s + 1);
    len = strlen(s + 1);
    r[0] = '@';
    for (int i = 1; i <= len; ++ i)
        r[i * 2 - 1] = '#', r[i * 2] = s[i];
    r[len * 2 + 1] = '#';
    lenr = len * 2 + 1;

    manacher();

    printf("%d\n", ans);
    return 0;
}
posted @   chzhc  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
levels of contents
点击右上角即可分享
微信分享提示