Manacher 算法学习笔记
Manacher 算法是一种支持在 时间内求出一个长度为 的字符串的最长回文子串的算法。
需要注意的是,Manacher 算法只能求形如 类的回文串,而不能处理形如 类的回文串,也就是只能求长度为奇数的回文串。所以,在最初需要对原串进行预处理,用没有出现过的字符将原串中的相邻两个字符隔开,同时要用两个不同的字符分别放在原串的开头和结尾。
如原串为 ,转化后就可以是 $#a#b#b#c#a#c#^。此时再和原串中的回文子串一一对应,会发现原串的回文串长度就是新串的回文串半径的长度减一。
回到 Manacher 算法,它本身的思想是从前往后一个个递推,求出 表示以 为中心的最长回文串的半径长度。而 自然就是答案。(下文简记回文子串 表示以 为中心的最长回文子串)
从前往后递推的时候,维护一个右边界最靠右的回文子串,记为 ,简称 ,而回文子串的中心就被称为 。
由于这个回文子串是在 之前枚举到的,所以必然有 。但是 和 的大小关系就是不确定的,需要分情况讨论。
. 。由于此时 在回文子串 内,那么就可以在这个回文子串中找到 的对称点,记为 ,那么根据简单的数学知识,可知 。
(1).,即 回文子串 完全在回文子串 内,那么根据回文子串内部 左右两边对称的性质,就有 。如果此时恰好 取到 ,那么就无法保证 是否与 相等,因此是 。
(2).,即回文子串 的一部分前缀不在回文子串 内。此时一定满足 。证明如下图所示:
可以推出最左边和最右边的红色部分的子串对称,这与 是回文子串 的右边界矛盾。故一定有 。但是根据回文子串 与左边的回文子串 对称,只能是 。
综上,当 时,有 。
.。此时回文子串 就有可能很大。直接从 开始枚举即可。
综合以上两种情况,我们先求出 的下界,并不断往外拓展,直到不能扩展为止。如果此时 ,就需要更新 和 。
下面来简单证明一下为什么这样做的时间复杂度是 。在情况 中,只有当 恰好与 相等时扩展操作会执行多次。那么此时的 就会更新成 ,实际上也就是 在单调递增。而情况 也是如此,故扩展操作最多执行 次。那么复杂度也就稳定在 。
code:
#include<cstring>
#include<algorithm>
using namespace std;
const int N=32000005;
int ans,n,p[N];char a[N],b[N];
void init()
{
int k=0;b[k++]='$',b[k++]='#';
for(int i=0;a[i];i++) b[k++]=a[i],b[k++]='#';
b[n=k]='^';//$和^主要是防止边界问题,如果不想加也可以在while循环里特判一下
}
void manacher()
{
int mr=0,mid;//在代码实现中,通常会将mr定义为最长回文子串最右边再过去一个字符的下标,因此下面部分代码与思路有所不同
for(int i=1;i<=n;i++)
{
if(i<mr) p[i]=min(mr-i,p[2*mid-i]);
else p[i]=1;
while(b[i+p[i]]==b[i-p[i]]) p[i]++;
if(i+p[i]>mr) mr=i+p[i],mid=i;
ans=max(ans,p[i]-1);
}
}
int main()
{
scanf("%s",a);init();manacher();printf("%d\n",ans);
return 0;
}```
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效