博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Manacher算法学习笔记
阅读量:6905 次
发布时间:2019-06-27

本文共 3711 字,大约阅读时间需要 12 分钟。

前言

Manacher(也叫马拉车)是一种用于在线性时间内找出字符串中最长回文子串的算法

算法

一般的查找回文串的算法是枚举中心,然后往两侧拓展,看最多拓展出多远。最坏情况下$O(n^2)$

然而Manacher能够充分利用回文的性质

首先,回文分为奇回文(比如$aba$)和偶回文(比如$abba$),如果分开来讨论会很麻烦。

于是我们在原串的首尾以及每两个字符之间各插入一个原串中没有出现过的字符。比如$abbbac$,变成$\%a\%b\%b\%b\%a\%c\%$

那么这样的话,上面的$aba$变成$\%a\%b\%a\%$,$abba$变成$\%a\%b\%b\%a\%$,都变成了奇回文

我们定义$p[i]$为以$i$为中心的回文串的最长半径,比如串$\%a\%b\%a\%$,其中$b$为第四个字符,则$p[4]=4$(因为以他为中心的最长回文串是$\%a\%b\%a\%$,而这个回文串的半径为4)

那么我们原串中以这个位置为中心的最长回文串长度就是$p[i]-1$(一个要去掉$\%$,然后半径的话要防止中心被多算一次)

那么现在的问题就是怎么快速求出$p[i]$了

我们假设$pos$是一个已经记录的值,$R$为以$pos$为中心的回文串的最长右边界,然后现在要求$p[i]$

$j$是$i$关于$pos$的对称点,也就是说$j=2*pos-i$

那么这个时候分为两种情况

1.$j$的最长回文没有跳出$L$

因为$[L...R]$是一个回文串,所以$i$的回文和$j$的回文相同,即$p[i]=p[j]$

2.$j$的最长回文越过了$L$

如果在这种情况下,$j$的最长回文就不一定是$i$的最长回文了。然后黄色那块肯定还是满足条件的

所以综上,$p[i]=min(p[2*pos-1],R-i)$

然后剩下的那部分怎么办?暴力直接算

我没口胡,真的

时间复杂度

时间复杂度是$O(n)$的

为啥嘞?

如果$p[i]$能直接求肯定是$O(1)$的

然后如果需要上暴力那么每一次都能让$R$严格右移

因为串长只有$O(n)$,所以暴力次数最多$O(n)$

上板子吧,板子抄的zzk大爷的

1 // luogu-judger-enable-o2 2 //minamoto 3 #include
4 #include
5 #include
6 using namespace std; 7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 8 char buf[1<<21],*p1=buf,*p2=buf; 9 template
inline bool cmax(T&a,const T&b){
return a
=1&&i+p[i]<=len&&now[i-p[i]]==now[i+p[i]]) ++p[i];24 if(i+p[i]>R) pos=i,R=i+p[i];25 }26 int mx=0;27 for(int i=1;i<=len;++i) cmax(mx,p[i]-1);28 return mx;29 }30 int main(){31 // freopen("testdata.in","r",stdin);32 read(s);33 printf("%d\n",manacher(s));34 return 0;35 }

不过我有个地方还没弄明白,就是上面求$p[i]$的时候,我写成这样也能A

1     for(int i=1;i<=len;++i){2         if(i
=1&&i+p[i]<=len&&now[i-p[i]]==now[i+p[i]]) ++p[i];4 if(i+p[i]-1>R) pos=i,R=i+p[i]-1;5 }

话说如果我写成 p[i]=min(p[2*pos-i],R-i+1) 我还能理解为是开区间和闭区间的不同……话说一个闭区间一个开区间为啥没问题啊……

先坑着,等会了再来填坑

首先双回文串肯定是拼接而成的

那么我们就记录一下每一个字符分别作为回文串的最左端(最右端)时,对应的中心最左(最右)在哪里

然后就可以通过拼接得到双回文串了

1 //minamoto 2 #include
3 #include
4 #include
5 using namespace std; 6 template
inline bool cmax(T&a,const T&b){
return a
=1&&i+p[i]<=len&&now[i-p[i]]==now[i+p[i]]) ++p[i];17 if(i+p[i]>R) pos=i,R=i+p[i];18 }19 }20 int main(){21 // freopen("testdata.in","r",stdin);22 scanf("%s",s+1);23 manacher(s);24 for(int i=1,pos=1;i<=len;++i)25 for(;pos<=i+p[i]-1;++pos) L[pos]=i;26 for(int i=len,pos=len;i;--i)27 for(;pos>=i-p[i]+1;--pos) R[pos]=i;28 for(int i=1;i<=len;++i) cmax(ans,R[i]-L[i]);29 printf("%d\n",ans);30 return 0;31 }

因为题目要求的是奇数回文,所以连$\%$都没必要加了,直接上manacher就好了

然后因为一个半径为$n$的回文串存在,那么$n-1$,$n-2$的回文串也必定存在

所以要开一个桶存一下前缀和

然后还要用一下快速幂

然后就差不多了

1 //minamoto 2 #include
3 #include
4 #include
5 #define ll long long 6 using namespace std; 7 const int N=1e6+5,mod=19930726; 8 char s[N];int p[N],c[N],n;ll ans=1,sum=0,k; 9 inline ll qpow(ll a,ll b){10 ll res=1;11 while(b){12 if(b&1) (res*=a)%=mod;13 (a*=a)%=mod,b>>=1;14 }15 return res;16 }17 int main(){18 // freopen("testdata.in","r",stdin);19 scanf("%d%lld",&n,&k);20 scanf("%s",s+1);21 for(int i=1,pos=0,R=0;i<=n;++i){22 p[i]=i
=1&&i+p[i]<=n&&s[i-p[i]]==s[i+p[i]]) ++p[i];24 if(i+p[i]-1>R) pos=i,R=i+p[i]-1;25 ++c[2*p[i]-1];26 }27 (n&1)?0:(--n);28 for(int i=n;i;i-=2){29 sum+=c[i];30 if(sum>k){(ans*=qpow(i,k))%=mod;break;}31 (ans*=qpow(i,sum))%=mod,k-=sum;32 }33 printf("%lld\n",sum

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9628247.html

你可能感兴趣的文章
online_judge_1055
查看>>
Python第三方库matplotlib(2D绘图库)入门与进阶
查看>>
请给出一个时间为O(nlgk)、用来将k个已排序链表的算法。此处n为所有输入链表中元素的总数。...
查看>>
JS判断是否是微信打开页面
查看>>
event.returnValue=false与event.preventDefault()
查看>>
CSAPP:Binary Bomb
查看>>
Hibernate SQL方言 (hibernate.dialect) Spring配置文件applicationContext.xml
查看>>
我的成就故事
查看>>
ASIHTTPRequest 详解, http 请求终结者 (转)
查看>>
Python 函数(补充)
查看>>
webpack 转换 ES6高级语法 bable插件 module rules
查看>>
添加占位图片
查看>>
正则表达式
查看>>
Lepus(天兔)监控MySQL部署
查看>>
Selenium应用代码(登录)
查看>>
Node.js权威指南 (4) - 模块与npm包管理工具
查看>>
CLR Via CSharp读书笔记(18):定制Attributes
查看>>
Java_捕获到异常之后_抛出运行时异常_的好处
查看>>
JavaScript表格、表单
查看>>
中文词频统计
查看>>