Manacher

· · 算法·理论

Manacher 算法

Manacher 算法 是一个用来查找一个字符串中的最长回文子串(不是最长回文序列)的线性算法。它的优点就是把时间复杂度为 O(n^2) 的暴力算法优化到了 O(n)。首先先让我们来看看最原始的暴力扩展,分析其存在的弊端,以此来更好的理解 Manacher 算法

暴力匹配

优化暴力

二分加哈希

显然,前面都不是正题。

Manacher 算法

经典问题:P3805 【模板】manacher。

过程

Step. 1 输入及处理

  1. 读入字符串 str_原
  2. str_原 中包括头尾在内的所有字符间的间隔插入一个在原字符串内没有的字符。

Step. 2 从前往后推半径

修改好字符串后,我们开始正式操作。

在这之前,先定义几个概念:

维护 p_i

Manacher 算法需要维护最大回文半径。设以第 i 个字符为中点的最大回文半径

从前往后计算 p_i

假设我们已经计算完成了 p_1, p_2, p_3, \dots , p_{i-1}。在枚举 i 的过程中,我们维护 r 的最大区间 [l,r],其中:

l=m-p_m+1~~(m<i)\\ r=m+p_m-1~~(m<i)\\

如何扩展

p_{2m-i}<r-i+1,令 p_i=p_{2m-i}。\ 若 p_k 对应的回文区间包含 l,令 p_i=p_{r-i+1}。\ 并继续暴力扩展。

i>r,令 p_i=1。\ 若 1 \le r,令 p_i=min\{p_{2m-i},r-i+1\}。\ 并继续暴力扩展。

正确代码

#include<bits/stdc++.h>
using namespace std;
const long long MaxN=2.2e7+10;
long long n;
long long jump[MaxN];
long long l=0,r=-1;
long long ans=1;
long long cnt=1;
char str[MaxN],x;
int main(){ 
    str[0]='#';
    x=getchar();
    while(x>='a' && x<='z'){
        str[cnt++]=x;
        str[cnt++]='#';
        x=getchar();
        if(x==EOF) break;
    }

    for(long long i=1;i<cnt;i++){
        if(i<r) jump[i]=min(r-i,jump[r-i+l]);
        while(str[i+jump[i]+1]==str[i-jump[i]-1]) jump[i]++;
        if(i+jump[i]>r) r=i+jump[i],l=i-jump[i];
        ans=max(ans,jump[i]);
    }
    cout<<ans<<endl;
    return 0;
}