【学习笔记】manacher
众所周知,manacher 又叫“马拉车”算法,是一种线性求解最长回文子串的算法。
推荐结合模板阅读此文。
求最长回文子串,首先想到的是暴力。枚举子串的左右端点
观察发现,我们可以只枚举中点,然后同时向左右不断扩展,当无法再扩展时更新答案。
具体来说,我们求出所有的
但这样有一个问题:长度为偶数的回文串没有回文中心。针对这个问题,我们在字符串每个字符间加上一个“特殊字符”,保证这个字符不在原字符串的字符集中(下文取 |
作为这个特殊字符)。
拿样例 aaa
举例,加上特殊字符后整个字符串变成了这个样子:
|a|a|a|
这有什么好处呢?对于回文长度为偶数的子串,回文中心就为一个 |
。
所以整个串的
S: | a | a | a |
P: 1 2 3 4 3 2 1
可以推出,以
所以答案为
#include<bits/stdc++.h>
using namespace std;
const int N = 11e6 + 9;
char s[N << 1];
int p[N << 1],len,ans;
void read(){//读入字符串
char c = getchar();len = 1;
s[len] = '|';
while(c < 'a' || c > 'z')
c = getchar();
while(c >= 'a' && c <= 'z'){
s[++len] = c;
s[++len] = '|';//添加特殊字符
c = getchar();
}
}
int main(){
read();
for(int i = 1,mx = 0,id = 0;i <= len;i++){
p[i] = 1;
while(s[i - p[i]] == s[i + p[i]])//暴力扩展
p[i]++;
ans = max(ans,p[i] - 1);//更新答案
}
printf("%d",ans);
return 0;
}
但是这样还不是最终的 manacher 算法,我们如果想要得到一个线性算法,就要想如何线性求出
我们记在分别以前
接下来我们推一下以
设这个下标为
所以如果对称点
由上述我们可以得到如果
扩展完成之后,我们再按
总复杂度是线性的(不想证了)。
#include<bits/stdc++.h>
using namespace std;
const int N = 11e6 + 9;
char s[N << 1];
int p[N << 1],len,ans;
void read(){
char c = getchar();len = 1;
s[0] = '~';s[len] = '|';
while(c < 'a' || c > 'z')
c = getchar();
while(c >= 'a' && c <= 'z'){
s[++len] = c;
s[++len] = '|';//添加特殊字符
c = getchar();
}
}
int main(){
read();
for(int i = 1,mx = 0,id = 0;i <= len;i++){
p[i] = i <= mx ? min(p[(id << 1) - i/*i关于id的对称点*/],mx - i + 1) : 1;
while(s[i - p[i]] == s[i + p[i]])//暴力扩展
++p[i];
if(p[i] + i - 1 >= mx){//访问到的最右侧的下标大于mx,更新mx和id
mx = p[i] + i - 1;
id = i;
}
ans = max(ans,p[i] - 1);//更新答案
}
printf("%d",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!