manacher算法
manacher 算法
1 算法简介
manacher 算法是一个求字符串中最长回文连续子序列的算法,可以达到 \(O(n)\) 的复杂度。
2 算法
我们可以在每一个字符之间补上一个 ‘#’ 这样所有的回文串就都会变成长度为奇数的回文串,我们用 \(len_i\) 表示从 \(i\) 点能够扩展出的回文长度(包括自身)最长是多少,\(maxright\) 表示已经触及到的最右边的字符,\(mid\) 表示 \(maxright\) 所对应的对称轴。
如图:
然后我们发现,\(len_i\) 的值至少是 \(\min(len_j,maxright-i+1)\) 。然后我们就可以从这个位置开始扩展,直到不能扩展。
3 代码
在实际实现中,我们让 \(maxright\) 作为最右边字符在往右的字符,这样比较好写一点。
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 22000000
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
const int INF=0x3f3f3f3f;
inline int Min(int a,int b){
return a>b?b:a;
}
inline int Max(int a,int b){
return a>b?a:b;
}
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int len[N],maxright,mid,n,ans;
char a[N],s[N];
inline void prework(){
int len=strlen(a);
for(int i=0;i<len;i++){
s[i*2+1]=a[i];s[i*2]='#';
}
n=len*2;s[n]='#';
}
inline void manacher(){
for(int i=0;i<=n;i++){
if(i<maxright) len[i]=Min(len[(mid<<1)-i],len[mid]+mid-i);else len[i]=1;
while(i-len[i]>=0&&s[i-len[i]]==s[i+len[i]])len[i]++;
if(maxright<len[i]+i){maxright=len[i]+i;mid=i;}
}
}
//#a#a#a#
int main(){
scanf("%s",a);
prework();
manacher();
for(int i=0;i<=n;i++) ans=Max(ans,(len[i]*2-1)/2);
printf("%d",ans);
return 0;
}
注意这里 len[(mid<<1)-i]
为 \(j\) ,这里 len[mid]+mid-i
为 \(i\) 到最右边的距离。
4 复杂度证明
因为每一次执行第 \(28\) 行的 while
循环的时候,如果 len[i]
是从 len[mid]
那里转移过来的,那么这个 while
语句实际上不会执行。否则,那么 while
循环一定会把 maxright
想右扩展,而因为 maxright
是单调不减的,所以 while
语句均摊下来仍然是 \(O(n)\) 。