manacher算法学习总结

本文深入讲解Manacher算法,一种高效求解最长回文子串问题的方法。通过对比O(n^2)暴力算法,阐述Manacher算法如何避免重复计算,实现线性时间复杂度。文章详细解析算法思想,包括字符串预处理、状态转移方程及代码实现。
摘要由CSDN通过智能技术生成

马拉车,manacher算法学习总结

教练让我们自己学。。无奈

QWQ,暴梨呵呵

在没学manacher的时候大家会有什么暴力方法,,,网上认为最好的就是O(n^2)的算法,枚举每个点作为半径中心(半径指以半径中心向两边拓展的最大长度,要保证回文串性质),每个都向外拓展这样的话,每个点最多拓展(n/2)次,所以复杂度为O(n ^2)的。那么大家会发现,,这个每次拓展的话重复的特别多。在这里插入图片描述
比如,这个。大家可看到整个串就是一个回文串但是,也就是因为这样重复的就会特别多。
在前n/2个枚举是前n/2个a被一直重复拓展。其他的也是多的可怕!所以我们要消灭这个令人无语的重叠。所以就有了manacher。

QWQ manacher思想算法解析

不过说真的,我已开始看到这东西,what!马拉车是虾米东东,然后就在脑子里脑补了一幅这样的图。。在这里插入图片描述
不是马。。额我觉得是就行。。。好了开始正题先贴贴程序。学习机房大佬编出来的。。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a); i<=(b); ++i)
#define DEP(i,a,b) for(int i(a); i>=(b); --i)
#define MAXN 31000010
using namespace std;
void read(int &x){
    x=0; char c=getchar(); int f=1;
    for(;!isdigit(c);c=getchar()) if (c=='-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f;}
void read2(int &x,int &y){read(x),read(y);}
void read3(int &x,int &y,int &z){read(x),read(y),read(z);}
int p[MAXN],n; char s[MAXN],str[MAXN]; 
struct node{
    void init(){
        str[1]=str[2]='#';
        REP(i,1,n) str[(i<<1)+1]=s[i],str[(i<<1)+2]='#';
        n=(n<<1)+3; str[n]=0;}
    void manacher(){
        int mr=0,pla=0; for(;str[n]!=0;n++) str[n]=0;
        REP(i,1,n-1){
            if (mr>i) p[i]=min(p[2*pla-i],p[pla]+pla-i); else p[i]=1;
            for(;str[i-p[i]]==str[i+p[i]];++p[i]);
        if (p[i]+i>mr){mr=p[i]+i;pla=i;}}}
}q;
int main(){
    scanf("%s",s+1); n=strlen(s+1); q.init(); q.manacher(); int ans=-MAXN;
    REP(i,1,n-1) if (p[i]>ans) ans=p[i]; cout<<ans-1<<endl;  
return 0;}

首先,大家一般都会这么讲,所以我也就折磨开始了。拿到一个字符串,你会发现,他有奇有偶。这时候我们就要把他统一下,不然,哈哈,体会下数学分类讨论的毒瘤吧。。。所以我们要干嘛捏,我们要插字符,把他硬生生的搞成偶的。
比如在这里插入图片描述我们要把它搞成更美丽的样子比如这样。在这里插入图片描述这样就一定是偶的。就是和其他人不一样。。。就完美的统一了奇偶。这段操作体现为init()里的操作

void init(){
       str[1]=str[2]='#';
       REP(i,1,n) str[(i<<1)+1]=s[i],str[(i<<1)+2]='#';
       n=(n<<1)+3; str[n]=0;} 

然后就来拉车了,(嘿咻嘿咻嘿咻)
所以你不得不承认,数学的分类讨论真的很牛,,及时你对他又爱又恨。。我们这里要分类了,哎凄凄惨惨戚戚,乍暖还寒时候,最难将息。。
分三类,这里很有优化的味道,,你就把他当暴力优化,就是减枝,也就剪得幅度大了一点,把没用的多余的都减掉了。。。额。。。
这里说明一下MR是我们在搜到i这个位置之前的最大半径,那么PLA就是他的位置了。。。嗯呢

第一大类(是不是感觉不友好)

当 i < MR 的时候

这是后我们就可以愉快的跳了。怎么说呢。
比如这样,在这里插入图片描述这是其中的一个大的回文串,这个时候,比如我们的i是第二个“C”…也不可能是第一个呀。。这时候你会发现咦,他是回文串,那么,枚举到第二个‘C’的时候我们已经把第一个“C”搞出来了,然后再看看他们两的四周咦,他们都在最大的回文串里面,两边好像,都一样,都一样!!!最关键的一点。。所以我们就可以直接跳到最后一个“#”后面去了。

第一小类(我也不想呀)

发现前面的和他关于PLA对称的那个字母,在最大的回文串里就已经终止了,就是说他的ML(最大左边界)都没超过最大回文串,那么,既然是对称的,我们就,额,直接复制粘贴走人。。好爽!!

第二小类(手打的有点酸)

当我们发现,关于对称的那个字母,他的ML出去了,不在最大回文串里,我们就感觉不太好了,,因为,,对于i这个字母,他是不可能再往后拓展了,因为如果再向后拓展就证明还是对称的,那么最大字符串也就可以在拓展了。。所以这个时候我们要自立根生。。。(暴瘦!!暴瘦暴瘦!!暴瘦!)
虽然BB了那么多但是代码额。。

 if (mr>i) p[i]=min(p[2*pla-i],p[pla]+pla-i); else p[i]=1;

第二大类(终于)

最后就是如果i已经超过了MR那么我们就要自立根生。。。(暴瘦!!暴瘦暴瘦!!暴瘦!)

好吧没了。。。。
而暴瘦的代码也很短

 for(;str[i-p[i]]==str[i+p[i]];++p[i]);

然后最后更新一下PLA和MR以保证后面可以继续不重复,跳着向前走

        if (p[i]+i>mr){mr=p[i]+i;pla=i;}}}

总结下也就这么点东西。。。

void manacher(){
        int mr=0,pla=0; for(;str[n]!=0;n++) str[n]=0;
        REP(i,1,n-1){
            if (mr>i) p[i]=min(p[2*pla-i],p[pla]+pla-i); else p[i]=1;
            for(;str[i-p[i]]==str[i+p[i]];++p[i]);
        if (p[i]+i>mr){mr=p[i]+i;pla=i;}}}

纤夫终于完成了他的工作。。。然后找下最大值,,,输出最大值-1。。。。。
所以你主要到#的影响了嘛,就在这个-1里面,至于原因,很好想。。可以参考这篇大佬的文章
膜拜大佬
所以结束了
大家可以搞搞这题
最长双。。。。
THE END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值