Manacher学习笔记
想说的话
这个东西基本写一次忘一次,和\(KMP\)一样不好想。不是很懂,希望能给自己一个复习的思路。
写在\(2021\)省选前。
\(2021.4.8,xxbbkk\)
大体介绍
这是一个神奇的算法,硬生生把基本没有优化空间\(O(n^2)\)的回文串处理加速成线性.
基本介绍
Manachar算法主要是处理字符串中关于回文串的问题的,它可以在 \(O(n)\) 的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍长度的新串,在每两个字符之间加入一个特定的特殊字符,因此原本长度为偶数的回文串就成了以中间特殊字符为中心的奇数长度的回文串了。
以上来自百度百科。
len数组
核心数组 。储存以这个位置为中心的回文串长度半径 。
pos||mid
目前的中心点,指的是(坐标+坐标的回文半径)最大(右端点最远)的位置。
mxr
\(max\)_\(right\) ,最大的右端点。
算法流程
插入特殊字符
开头插一个@#,中间每隔一个字符就插一个#。
用来把原本奇数长度的回文串变成偶数长度。
处理len数组
核心操作 。核心代码就一句话:
len[i]=mxr>i?min(len[(pos<<1)-i],mxr-i):1;
如果目前扩展到的区间覆盖到了这个位置,那\(len\)就是\(min\) (这个点到右端点的距离,这个点关于\(mid\)的对称点的回文长度)。
好好理解。
后续
后面的就很好理解了,不用细讲。
代码
#include<bits/stdc++.h>
#define N (22000010)
using namespace std;
string s="",s1="";
int ans,pos,mr,len[N];
int main(){
cin>>s1;
int n=(int)s1.length();
s+="@#";
for(int i=0;i<n;i++){
s+=s1[i];
s+='#';
}
for(int i=1;i<(n<<1);i++){
len[i]=mr>i?min(len[(pos<<1)-i],mr-i):1;
while(s[i+len[i]]==s[i-len[i]]) len[i]++;
if(i+len[i]>mr) mr=i+len[i],pos=i;
ans=max(len[i]-1,ans);
}
printf("%d\n",ans);
return 0;
}
代码很简单,理解很困难。
一些题目
会继续加的......