Manacher算法
Manacher 算法可在 解决最长回文串的问题。
通过预处理 表示以 为中心的回文串向两边延伸的最长长度来解决原问题。
如对于字符串 abcba
(下标从开始),,即 。
算法流程如下:
改造字符串
在开头插入 $
,末尾插入 !
,每个字符的左边和右边都插入一个 #
。
例如:
abcdefg 改造为: $#a#b#c#d#e#f#g#!
在开头和末尾插入两个不同的字符,可以避免边界问题。而在相邻字符插入 #
,则是为了处理长度为偶数的回文串,例如:aa
转化为 #a#a#
,即变为以第二个 #
为中心的回文串。
预处理
在预处理 时,需要维护一个表示当前右端点最右边的回文串区间,不妨用 表示。
假设现在正在处理 ,那么分为两种情况:
对于 在 中,可以找到一些性质,记 表示 关于 的对称点(注意:此时 已经求出),那么分为以下几种情况:
的回文长度未超出 (下图蓝色为回文部分)
由于 本身为回文串,所以在对称位置,回文情况是一致的,那么直接 即可。
的回文长度超出
那么我们只能确认图中(即在内的部分)蓝色部分为回文,至于红色部分(超出区间部分),我们无法确认,只能通过暴力枚举处理。
直接暴力往两边扩展即可。
计算完 记得更新区间。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=3e7; int n,d[N]; string s; char ch[N]; int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>s; ch[0]='$';//改造字符串 for(int i=0;i<s.size();i++) { ch[++n]='#'; ch[++n]=s[i]; } ch[++n]='#'; ch[++n]='!'; d[1]=1; for(int l=1,r=1,i=2;i<n;i++) { if(i>=l&&i<=r) d[i]=min(d[l+r-i],r-i+1);//l+r-i即为i',r-i+1即i到右边界距离 while(ch[i+d[i]]==ch[i-d[i]]) d[i]++;//暴力扩展 if(i+d[i]-1>=r) {r=i+d[i]-1; l=i-d[i]+1;}//更新区间 } int ans=0; for(int i=1;i<=n;i++) { ans=max(ans,d[i]-1); } cout<<ans; return 0; }
扩展应用
裸的Manacher算法只能求出以一个字符为中心向两边延伸的最长回文长度,但是没法求出以某个点为起点的最长回文长度,需要通过递推的方式求解。
首先预处理出上文的 数组,然后用 和 计算出 ,此时计算出的所有区间,都是尽可能长的区间,也就是说:如果 是回文,那么也一定是回文,而目前的做法只能处理出 ,处理不出 。
所以考虑用 解决问题,设 表示以 为开头的最长长度,先用 计算出一部分 。
从上文 和 的例子也能得到一些启发,即前一个开头的最长回文损失两个(一头一尾)就变成当前位置回文长度,即:
当然,在 Manacher 算法中,由于字符之间用 隔开,所以实际改写为:
类似例题:P4555 [国家集训队] 最长双回文串
提交记录:记录详情
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!