Manacher 算法
算法介绍
算法思想
和其他大多数算法一样,
经典例题:给定一个字符串
。求出其最长回文子串
。
该题暴力算法法是,枚举
我们思考一个回文串的性质。对于一个回文串,其必然有一个回文中心,满足这个回文串关于回文中心对称。回文中心到字符串两端的距离成为回文半径。回文中心分为两类,分别是在一个字符上:例如
对于任何一个回文串,回文中心都只有一个。我们枚举回文中心,定义
算法的运行,我们分两种情况
-
:因为 位于我们最靠右回文串边界之外。无法利用已知信息求解。暴力枚举其回文半径,找出 。 -
:因为 位于一个回文串内,回文中心为 。我们再分两种情况讨论
-
以
为回文中心的最长回文子串 完全包含在 。因为回文串的性质。 左边必然有一个回文串关于 对应,记为 。因为回文串只有一个回文中心。所以 的回文中心与 的回文中心关于 对称。我们用 表示 的回文中心。则 。也可以得到 -
以
为回文中心的最长回文子串 不完全包含在 。但 在 中回文的部分一定和 对应。且 。根据回文的对称性,虽然无法保证 。但可以保证 。找到 的最小值后无法保证回文半径最大,继续暴力枚举
最后,在计算完
时间复杂度分析:
虽然有很多暴力的拓展,但每次拓展后,
答案计算
最后还有答案计算,我们令操作中,插入了辅助字符的字符串为
例题:
例 :【模板】manacher
模板题
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N=3e7+10;
char s[N];
int p[N],mid,r=-1,n,ans=0;
void read(){
char ch=getchar();
s[n]='~',s[++n]='#';//首段加上一个‘~’是为了处理越界情况
while(ch>='a'&&ch<='z')s[++n]=ch,s[++n]='#',ch=getchar();
s[++n]='#';
return;
}
int main(){
read();
for(int i=1;i<=n;i++){
if(i>r)p[i]=1;
else p[i]=min(p[mid*2-i],r-i+1);
while(s[i-p[i]]==s[i+p[i]])p[i]++;
if(i+p[i]-1>r)r=p[i]+i-1,mid=i;
if(p[i]>ans)ans=p[i];
}
printf("%d\n",ans-1);
return 0;
}
例 :[国家集训队] 最长双回文串
对于双回文串中的每个“断点”(字符 #
的位置)。找到以其为左右端点的最长回文串
点击查看代码
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int N=3e5+10;
char s[N];
int n,mid,r=-1,p[N],L[N],R[N],ans;
void read(){
char ch=getchar();
s[++n]='~';s[++n]='#';
while(ch>='a'&&ch<='z')s[++n]=ch,s[++n]='#',ch=getchar();
return;
}
signed main(){
read();
for(int i=1;i<=n;i++){
if(i>r)p[i]=1;
else p[i]=min(p[mid*2-i],r-i+1);
while(s[i-p[i]]==s[i+p[i]])p[i]++;
if(i+p[i]-1>r)r=p[i]+i-1,mid=i;
L[i-p[i]+1]=max(L[i-p[i]+1],p[i]-1);
R[i+p[i]-1]=max(R[i+p[i]-1],p[i]-1);
}
for(int i=n-2;i>=2;i-=2)R[i]=max(R[i+2]-2,R[i]);
for(int i=4;i<=n;i+=2)L[i]=max(L[i-2]-2,L[i]);
for(int i=2;i<=n;i+=2)if(L[i]&&R[i])ans=max(ans,L[i]+R[i]);
printf("%lld\n",ans);
return 0;
}
例 :[国家集训队] 拉拉队排练
如果我们在算法的过程中,用
因为题目要求一个“小团体”长度必须为单数。所以要对一些情况特判。
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N=2e6+10,mod=19930726;
typedef long long ll;
ll len,k,n,mid,r=-1,p[N],maxn=0,sum[N],ans=1,z;
char s[N];
void read(){
char ch=getchar();
s[n]='~',s[++n]='#';
while(ch<'a'||ch>'z')ch=getchar();
while(ch>='a'&&ch<='z')s[++n]=ch,s[++n]='#',ch=getchar();
return;
}
ll ksm(ll a,ll b){
ll ans=1;
while(b){
if(b&1)ans=(a*ans)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int main(){
scanf("%lld %lld",&len,&k);
read();
for(int i=1;i<=n;i++){
if(i>r)p[i]=1;
else p[i]=min(p[2*mid-i],r-i+1);
while(s[i-p[i]]==s[i+p[i]])p[i]++;
if(i+p[i]-1>r)r=i+p[i]-1,mid=i;
if(s[i]!='#')sum[p[i]-1]++,maxn=max(maxn,p[i]-1);
}
for(int i=maxn;i>=1&&k>0;i--){
sum[i]+=sum[i+2],z=min(k,sum[i]);
ans=(ans*ksm(i,z))%mod;
k-=z;
}
if(k>0)printf("-1\n");
else printf("%lld\n",ans);
return 0;
}
The End
感觉还比较有用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】