Manacher 算法模板

简介

  • 在字符串的题目中,有时会遇上 回文串 这样一个名词

  • 顾名思义,回文串 就是 正读和反读都一样的字符串

  • 最长回文子串 ,就是在一个字符串的所有子串中,是回文串且长度最长的那个

  • 求最长回文子串最普通的方法是 O(N2) ,即枚举一个点往两边扩展

  • 但是在有些题目中,N 却十分的大

  • 那么我们就要用到 时间空间复杂度都是 O(N) Manacher 算法

用法

  • 在处理回文串时,我们常常会被 中间字符是一个还是两个 这样的问题困扰

  • 但是在机智的 Manacher 算法 中,这个问题得到了完美的解决

  • 在每两个字符中间插入一个不会出现的分隔符(如:#)

  • 之后在头尾插入一个还是没有出现的分隔符(如:*)来防止 While 出界

  • 这样处理起来就方便很多了!

  • 设读入的字符串为 s[i]

  • 记录 p[i] 表示 以 s[i] 为中心往两边扩展的最大长度

  • 观察可知,实际的回文串长度即为当前的 p[i]1

  • 再记录一个数 id p[id]+id 表示在 i 位置前所有回文串中能延伸到的最右端的位置

  • 如下图:

Manacher

  • 算法核心就是:

  • if(p[id]+id>i) p[i]=min(p[id2i],p[id]+idi); else p[i]=1;

  • 当之前所有回文串中能延伸到的最右端覆盖过 i 时,则取最小值,否则 p[i]=1 ,及自己本身

  • 这样不断维护 p[i] id ,就能在 O(N) 内求出 最长回文子串 了!

  • 至于为什么时间是线性的,由于最有端 p[id]+id 最多只能移动 N 次,

  • 有效移动的操作就严格线性啦!!

  • 下面附上模板:

void Manacher()
{
    scanf("%s",s);
    int len=strlen(s);
    for(int i=len;i>=0;i--)
    {
        int k=i*2+1;
        s[k+1]=s[i],s[k]='#';
    }//插入分隔符
    len*=2;
    s[ans=id=0]='*';
    for(int i=2;i<=len;i++)
    {
        if(p[id]+id>i) p[i]=min(p[id*2-i],p[id]+id-i); else p[i]=1;
        while(s[i-p[i]]==s[i+p[i]]) p[i]++;
        if(p[i]+i>p[id]+id) id=i;
        if(p[i]>ans) ans=p[i];
    }//处理、计算
}
  • 注释: s[i]p[i]id 如题意义, ans 表示 最长回文子串 的长度,而 len 是原串长度

总结

  • 这个 Manacher 算法效率极高,时间空间都是 O(N) 线性的

  • 再者代码极短,所以使用起来十分方便,应多多使用!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值