Loading

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)\)

posted @ 2021-04-15 18:18  hyl天梦  阅读(79)  评论(0编辑  收藏  举报