浅谈 Manacher
从某种方面来说,Manacher 算法是朴素 暴力算法的优化。。。
那就得先了解一下 Manacher 的朴素算法------
朴素算法
枚举中心点并不断向外展开(例如:)
缺点:
- 时间复杂度:———慢
- 不是特别好处理长度为偶数的回文串———菜
Manacher
想要优化,首先得解决几个问题:
如何处理长度为偶数的回文串?
可以这样:在每个字符串间及开头结尾加上一个特殊字符(例子:aaaa
~#a#a#a#a#
)
考虑例子中为什么开头第一个地方有个 ~
?
这个后面代码中再说(防止数组越界)。。。
如何使时间复杂度降到线性?
记录一个数组p[i]
表示以 为回文中心的回文半径。
现在就是处理来到 这个点,如何转移p[i]
?
我们在这里维护一个当前回文串最右端点 ,和其对称中心 。
当 时
因为
所以
我们通过以 为对称轴得到一个与当前的 对称的点 (这个对称点点的p[]
已经处理出来)来转移p[i]
,同时还能向外扩展:while(s[i-p[i]]==s[i+p[i]])++p[i];
求 就用初一的中点公式:
所以 。
当 时
,就是不能向外扩展(只有自己本身的长度)。
板子代码:
#include <bits/stdc++.h>
using namespace std;
const int N=3e7+5;
int n,p[N];
char s[N],st[N];
int cnt=1;
void init()
{
s[0]='~';
s[cnt++]='#';
for(int i=0;i<n;i++)
{
s[cnt++]=st[i];
s[cnt++]='#';
}
}
int main()
{
cin>>st;
n=strlen(st);
init();
int ans=-1;
for(int i=1,r=0,mid=0;i<=cnt;i++)
{
if(i<=r)p[i]=min(p[2*mid-i],r-i+1);
while(s[i-p[i]]==s[i+p[i]])++p[i];//解释一下:s的第一位那个~,就是在while循环中防止越界
if(i+p[i]>r)r=p[i]+i-1,mid=i;
if(ans<p[i])ans=p[i];
}
cout<<ans-1;
return 0;
}
小牛试刀
P6216 回文匹配
这题不是特别好像,考虑要想发挥出字符串真正的线性魅力,一般需要一些线性算法才能更加完美。
这题加的是二次前缀和。
首先用 KMP算法 求出 在 中出现的每一个位置,并准备第一次前缀和 (在每个出现位置的左端点 )。其次,再用 Manacher 求一下每个回文半径。最后,发现其实这个答案形如 ,
整理得 ,
发现可以将前缀和数组再次进行前缀和来得到答案。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!