manacher 算法总结
测试一下这个博客园的功能(图片好像只能在洛谷上看,有时间就改)
manacher 算法总结
题目大意
给定一字符串,求其最长回文串长度
方法对比
暴力效率:,优化后为
manacher效率:
算法思想
回文串有两种:奇回文与偶回文
分类讨论太麻烦,主要是我不会,于是我们就统一为奇回文
如何统一
例:
abbab
偶回文:abba 奇数回文:bab
我们在中间各插入一个符号,例如#号
则原式变为:
#a#b#b#a#b#
此时奇回文还是已某个子母为中心的回文,如:
bab->#b#a#b#
此时这个回文仍以a为中心
通过观察可以发现,偶回文变成已#为中心的奇回文,如:
abba->#a#b#b#a#
此时这个回文已第三个#号为中心
我们把原先的串命名为 串,修改后的串命名为 串,定义数组
表示在 串中以 为中心最多能扩散的半径(包括 本身)
例如:
t: # a # b # b # a # b #
p: 1 2 1 2 5 2 1 4 1 2 1
可是此时 与 不同,怎么通过 来还原长度呢?
还原长度
先上结论:最长回文长度 为
为什么是 呢?
如果是奇回文:
#b#c...#b#a#b...c#b#
右边每一个字母都可以与对应左边字母的右边#消除
最后剩下最左边的#,所以为
如果是偶回文:
#b#c...d#a#a#a#d...c#b#
按照奇回文的思路我们也可以得到 这个结论,这里可以再自己手动推一下
现在大致思路是这样,可是怎么求 呢,这是manacher精粹之一
对称思维
我们首先假设我们进行到 , 且 已知
则前面 必存在一个 ,使得 最大,记为
即:
注意, 可以为 到 任意一位置
图中黑色矩形为 串,蓝色矩形代表子母 ,红色矩形代表子母 ,红色框架代表已 为中心的最长回文串,其中右边界记为
同时,以 为原点两边会出现对称情况,所以 必然会出现其对称点
图中绿色矩形代表与蓝色矩形 相等且围绕 对称的子母 , 黄色线代表两点对称
此处直接给出i'的公式,有时间可以自己证
具体是运用了原点对称的思想
此时 也必然有自己的管辖范围(即以 为中心的最长回文长度),我们分两种情况讨论
情况1: 管辖范围在 的管辖范围以内
则:
因为此时 的管辖范围必然与i的管辖范围以 为原点一一对称,详见图:
图中绿色框架代表 管辖范围,蓝色框架代表 管辖范围,下面的黄色线代表绿色框架与蓝色框架以 为中心的一一对应
情况2: 管辖范围不在 管辖范围以内
则首先, 至少为
那么超出部分(紫色部分)怎么办呢,暴力呗
图中紫色区域代表超出部分
此时我们就可以处理所有 了
边界问题
可以看出,我们再求 和 时可能越出左边界
求紫色区域时可能越出右边界,则我们在 串左右分别加上两个字符,例如+和-
核心思路大致如此
核心代码
for(int i=1;i<=n;++i)
{
p[i]=1;
if(i<=mx)
p[i]=min(p[2*id-i],mx-i);
while(t[i-p[i]]==t[i+p[i]])
++p[i];
if(i+p[i]>mx)
id=i,mx=i+p[i];
}
效率分析
双重循环,看起来是,但事实上每次进入while循环, 值必改变,而 值最大为 ,所以时间效率为(不考虑常数)
完整代码
#include<bits/stdc++.h>
using namespace std;
char s[11000010];
char t[25000010];
int p[25000010];
int l,mx,id,ans;
int main()
{
// freopen("tiaoshi.in","r",stdin);
// freopen("tiaoshi.out","w",stdout);
scanf("%s",s+1);
t[0]='+';
l=1;
for(int i=1;s[i];++i,++l)
{
t[l]='#';
t[++l]=s[i];
}
t[l]='#';
t[l+1]='-';
for(int i=1;i<=l;++i)
{
// printf("%c",t[i]);
p[i]=1;
if(i<=mx)
p[i]=min(p[2*id-i],mx-i);
while(t[i-p[i]]==t[i+p[i]])
++p[i];
if(i+p[i]>mx)
id=i,mx=i+p[i];
ans=max(ans,p[i]);
}
// printf("\n");
printf("%d",ans-1);
return 0;
}
参考代码2:
scanf("%s", c+1); n=strlen(c+1);
s[0]='-'; s[1]='#';
for(i=1, j=2; i<=n; ++i, ++j)
s[j]=c[i], s[++j]='#';
s[j]='+'; n=j-1; r=0;
for(i=1; i<=n; ++i)
{
if(i>r) a[i]=2;
else a[i]=min(a[2*j-i], r-i+1);
while(s[i-a[i]]==s[i+a[i]]) ++a[i];
if(i+a[i]-1>r) r=i+a[i]-1, j=i;
ans=max(ans, a[i]-1);
}
printf("%d", ans);
本文来自博客园,作者:zhangtingxi,转载请注明原文链接:https://www.cnblogs.com/zhangtingxi/p/15551651.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!