Manacher算法学习笔记
前言
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大爷的
// luogu-judger-enable-o2
//minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
const int N=1.1e7+;
int p[*N];char s[N],now[N*];
inline bool is(char c){return c!='\n'&&c!=EOF;}
inline void read(char *s){
int len=;char ch;while(is(ch=getc())) s[++len]=ch;s[++len]=;
}
int manacher(char *s){
int len=strlen(s+);
for(int i=;i<=len;++i) now[*i-]='%',now[*i]=s[i];
now[len=len*+]='%';
int pos=,R=;
for(int i=;i<=len;++i){
if(i<R) p[i]=min(p[*pos-i],R-i);else p[i]=;
while(i-p[i]>=&&i+p[i]<=len&&now[i-p[i]]==now[i+p[i]]) ++p[i];
if(i+p[i]>R) pos=i,R=i+p[i];
}
int mx=;
for(int i=;i<=len;++i) cmax(mx,p[i]-);
return mx;
}
int main(){
// freopen("testdata.in","r",stdin);
read(s);
printf("%d\n",manacher(s));
return ;
}
不过我有个地方还没弄明白,就是上面求$p[i]$的时候,我写成这样也能A
for(int i=;i<=len;++i){
if(i<R) p[i]=min(p[*pos-i],R-i);else p[i]=;
while(i-p[i]>=&&i+p[i]<=len&&now[i-p[i]]==now[i+p[i]]) ++p[i];
if(i+p[i]->R) pos=i,R=i+p[i]-;
}
话说如果我写成 p[i]=min(p[*pos-i],R-i+1) 我还能理解为是开区间和闭区间的不同……话说一个闭区间一个开区间为啥没问题啊……
先坑着,等会了再来填坑
首先双回文串肯定是拼接而成的
那么我们就记录一下每一个字符分别作为回文串的最左端(最右端)时,对应的中心最左(最右)在哪里
然后就可以通过拼接得到双回文串了
//minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
const int N=1e5+;
int p[*N];char s[N],now[N*];int L[N<<],R[N<<],len,ans;
void manacher(char *s){
len=strlen(s+);
for(int i=;i<=len;++i) now[*i-]='%',now[*i]=s[i];
now[len=len*+]='%';
int pos=,R=;
for(int i=;i<=len;++i){
if(i<R) p[i]=min(p[*pos-i],R-i);else p[i]=;
while(i-p[i]>=&&i+p[i]<=len&&now[i-p[i]]==now[i+p[i]]) ++p[i];
if(i+p[i]>R) pos=i,R=i+p[i];
}
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%s",s+);
manacher(s);
for(int i=,pos=;i<=len;++i)
for(;pos<=i+p[i]-;++pos) L[pos]=i;
for(int i=len,pos=len;i;--i)
for(;pos>=i-p[i]+;--pos) R[pos]=i;
for(int i=;i<=len;++i) cmax(ans,R[i]-L[i]);
printf("%d\n",ans);
return ;
}
因为题目要求的是奇数回文,所以连$\%$都没必要加了,直接上manacher就好了
然后因为一个半径为$n$的回文串存在,那么$n-1$,$n-2$的回文串也必定存在
所以要开一个桶存一下前缀和
然后还要用一下快速幂
然后就差不多了
//minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=1e6+,mod=;
char s[N];int p[N],c[N],n;ll ans=,sum=,k;
inline ll qpow(ll a,ll b){
ll res=;
while(b){
if(b&) (res*=a)%=mod;
(a*=a)%=mod,b>>=;
}
return res;
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%d%lld",&n,&k);
scanf("%s",s+);
for(int i=,pos=,R=;i<=n;++i){
p[i]=i<R?min(p[*pos-i],R-i):;
while(i-p[i]>=&&i+p[i]<=n&&s[i-p[i]]==s[i+p[i]]) ++p[i];
if(i+p[i]->R) pos=i,R=i+p[i]-;
++c[*p[i]-];
}
(n&)?:(--n);
for(int i=n;i;i-=){
sum+=c[i];
if(sum>k){(ans*=qpow(i,k))%=mod;break;}
(ans*=qpow(i,sum))%=mod,k-=sum;
}
printf("%lld\n",sum<k?-:ans);
return ;
}
Manacher算法学习笔记的更多相关文章
- Manacher算法学习笔记 | LeetCode#5
Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...
- Manacher 算法学习笔记
算法用处: 解决最长回文子串的问题(朴素型). 算法复杂度 我们不妨先看看其他暴力解法的复杂度: \(O(n^3)\) 枚举子串的左右边界,然后再暴力判断是否回文,对答案取 \(max\) . \(O ...
- C / C++算法学习笔记(8)-SHELL排序
原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...
- Johnson算法学习笔记
\(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...
- 某科学的PID算法学习笔记
最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...
- Johnson 全源最短路径算法学习笔记
Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...
- Manacher算法学习 【马拉车】
好久没写算法学习博客了 比较懒,一直在刷水题 今天学一个用于回文串计算问题manacher算法[马拉车] 回文串 回文串:指的是以字符串中心为轴,两边字符关于该轴对称的字符串 ——例如abaaba 最 ...
- 算法学习笔记——sort 和 qsort 提供的快速排序
这里存放的是笔者在学习算法和数据结构时相关的学习笔记,记录了笔者通过网络和书籍资料中学习到的知识点和技巧,在供自己学习和反思的同时为有需要的人提供一定的思路和帮助. 从排序开始 基本的排序算法包括冒泡 ...
- manacher算法学习(求最长回文子串长度)
Manacher总结 我的代码 学习:yyb luogu题目模板 xzy的模板 #include<iostream> #include<cstdlib> #include< ...
随机推荐
- PAT 1053 住房空置率 (20)(代码+思路)
1053 住房空置率 (20)(20 分) 在不打扰居民的前提下,统计住房空置率的一种方法是根据每户用电量的连续变化规律进行判断.判断方法如下: 在观察期内,若存在超过一半的日子用电量低于某给定的阈值 ...
- Luogu 2154 [SDOI2009]虔诚的墓主人
弄了很久,状态很烂…… 首先发现可用的点一共只有$1e5$个,所以可以离散化坐标来方便计算. 发现对于一个空格,设它的上.下.左.右分别有$u, d, l, r$个点,它产生的贡献是$\binom{u ...
- 201621123008 《Java程序设计》 第三周学习总结
1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词,如类.对象.封装等 关键词:类,构造函数,方法重载,方法覆盖,封装,继承,多态,类被加载的过程,static,abstract, ...
- 电商类Web原型制作分享-IKEA
IKEA是一个家居整合大型零售商,属于电商类官网.电商以展示商品.售后服务.购物流程为主.根据网站的图文方式排版,主导航栏使用的标签组,区域导航栏使用的是垂直选项卡,实现下拉弹出面板交互的功能. 本原 ...
- ubuntu 'yuan' update
# tsinghua university deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiv ...
- Quartz教程三:Job与JobDetail介绍
Quartz教程三:Job与JobDetail介绍 原文链接 | 译文链接 | 翻译:nkcoder | 校对: 本系列教程由quartz-2.2.x官方文档翻译.整理而来,希望给同样对quartz感 ...
- 关于多系统跨浏览器 BrowserStack 的使用
偶然在Scott Hanselman Blogs看到一篇关于 BrowserStack 博文,对于前端多浏览器测试. 现在拥有各自内核的浏览器越来越多,各自的特性也千差万别.如果作为一个前端攻城师想要 ...
- hdu-1150(二分图+匈牙利算法)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1150 思路:题目中给出两个机器A,B:给出k个任务,每个任务可以由A的x状态或者B的y状态来完成. 完 ...
- hdu-1128(数学问题,筛数)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1128 思路:从0,开始,每次求一个数x的d(x),然后判断如果x没有标记,则说明x没有由任意一个d(i ...
- MySQL通过游标来实现通过查询记录集循环
/*我们有时候会遇到需要对 从A表查询的结果集S_S 的记录 进行遍历并做一些操作(如插入),且这些操作需要的数据或许部分来自S_S集合*//*临时存储过程,没办法,不能直接在查询窗口做这些事.*/d ...