马拉车,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