【笔记+模板】 manacher算法

算法相关

目的:求最长回文子串的长度。

回文串:“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。

暴力算法:枚举子串,检查是否回文 O(n^3)

暴力算法+:枚举每一个字符及空隙作为对称轴,暴力求回文子串长度 O(n^2)

Manacher:Manachar算法主要是处理字符串中关于回文串的问题的,它可以在 O(n) 的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍长度的新串,在每两个字符之间加入一个特定的特殊字符,因此原本长度为偶数的回文串就成了以中间特殊字符为中心的奇数长度的回文串了。

算法流程

定义

t[]:原字符串
s[]:预处理后的字符串
r[i]:在s中,以i为对称轴,能够扩展的最长的对称半径(不包括i字符本身)

预处理

为了避免偶数长度的回文串对称轴是字符间隙的情况,我们在字符之间插入与题目无关的字符(如‘#’)

eg1:
    a   b   a
  # a # b # a #
r 0 1 0 3 0 1 0

eg2:
    a   b   b   a
  # a # b # b # a #
r 0 1 0 1 4 1 0 1 0

由示例易知,在s串中的r数组,就是在t串中的以i为对称轴的回文子串的长度;
求最长回文子串,只需求得最大的r即可

算法流程

定义变量pos为在1~i中的一个对称轴,满足pos+r[pos]最大,并将pos+r[pos]记做max_r

显然pos < i,处理到i字符
考虑i与max_r的关系
当i < max_r 时,记i关于pos的对称点为j,
(j+i)/2=pos -> j=pos*2-i;
1.i < max_r &&r[j]+i>max_r

这里写图片描述
由pos和max_r的对称定义可知,r[i]至少为i~max_r,暴力匹配 max_r以后的字符
更新 pos+max_r

2. i < max_r&&r[j]+i<=max_r
这里写图片描述
由对称可知 , r[i]=r[j];

3. i >= max_r
这里写图片描述
暴力按位匹配,求得r[i],并 更新 pos+max_r

在代码中,三种情况并没有详细划分

模板

void Manacher(){
    for(int i=0;i<len_t;i++){
        s[i<<1]='#';
        s[i<<1|1]=t[i];
    }
    len=len_t*2+1;
    s[len-1]='#';
    //预处理结束
    int pos=0,max_r=0;
    for(int i=0;i<len;i++){
        if(i<max_r) r[i]=min(max_r-i,r[pos*2-i]);
        //第1,2种情况,取min初始化r   对称点j 
        else r[i]=0;
        //第3种情况 
        while(i+r[i]+1<len&&i-r[i]-1>=0&&s[i+r[i]+1]==s[i-r[i]-1]) r[i]++;
        //     左右不越界                  拓展的左右相等           更新r       
        if(r[i]+i>max_r){
        //更新pos+max_r 
            max_r=r[i]+i;
            pos=i;
        }
    }
    return ;
}

例题

https://vjudge.net/problem/POJ-3974

代码

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;
const int N=1000000+500;
int len_t,len,ans,cnt,r[N<<1];
string t;
char s[N<<1];

void manacher(){
    for(int i=0;i<len_t;i++) {
        s[i<<1]='#';
        s[i<<1|1]=t[i];
    }
    len=len_t*2+1;
    s[len-1]='#';
    int max_r=0,pos=0;
    for(int i=0;i<len;i++){
        if(i<max_r) r[i]=min(max_r-i,r[pos*2-i]);
        else r[i]=0;
        while(i+r[i]+1<len&&i-r[i]-1>=0&&s[i+r[i]+1]==s[i-r[i]-1]) r[i]++;
        if(i+r[i]>max_r){
            max_r=i+r[i];
            pos=i;
        } 
        ans=max(ans,r[i]);
    }
    return ;
}
void init(){
    len_t=t.size();
    ans=-1;
    memset(r,0,sizeof(r));

}
int main(){
    while(cin>>t){
        if(t=="END") return 0;
        cnt++;
        printf("Case %d: ",cnt);
        init();
        manacher();
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值