Manacher算法和回文自动机(PAM)

这两个东西都是处理字符串回文子串问题的有力工具。

引入

如果让你求你个字符串的最长奇回文子串,怎么求?

我会\(n^2\)暴力!枚举对称中心点\(i\),再左右拓展枚举回文半径\(j\)

......
for(int i=0;i<len;i++) {
    int j=1;
    while(i-j>=0 && i+j<len && s[i-j]==s[i+j]) j++;
    ans=max(ans,j*2-1);
}
......

如果字符串长度\(len\)很大,这个暴力就不行了。

Manacher算法 马拉车

算法流程

其实这个算法也是暴力,只不过是在上面的基础上加了一些优化。

众所周知,暴力之所以超时是因为做了很多重复的计算,那我们考虑一下能不能利用之前算过的答案来进行优化。

假设现在枚举到\(i\),之前有个回文串,它的中心对称点为\(mid\),这个串的右边界为\(R\),是\(i\)之前所有回文串最靠右的

  • 如果\(i<R:\)

    那么我们可以找到\(i\)的对称点\(i'\),假设以\(i'\)为中心的回文半径为\(r(i')\)

    • 当以\(i'\)为中心的回文串并没有扩出去,可以发现\(r(i)=r(i')\)

      因为它们都是处在以\(mid\)为中心的回文串内,所以上面\(i'\)的那个回文串(矩形部分)放到\(i\)那里是一样的(因为它们关于\(mid\)对称)。

      而且\(i\)也不能继续扩展了,因为\(i'\)最多只能扩到\(r(i')\),再扩展两边就不一样了,那么对称到\(i\)这边也同理。

    • 当以\(i'\)为中心的回文串扩出去了呢?

      因为在\(R\)右边的字符是未知的,不知道能不能对应到\(i'\)那边,所以此时的\(r(i)=R-i+1\)

      然后我们就可以暴力扩展了,顺便更新\(mid=i\),右边界\(R=i+r(i)-1\)

  • 如果\(R<i:\) 继续暴力扩展及更新。

时间复杂度

可以发现每次要么拓展,要么\(O(1)\)得到答案,当\(R\)扩展到\(len\)时,就不能执行\(while\)循环了,所以总的复杂度是\(O(n)\)

一些补充

可是通常题目求的不是奇回文子串,而是奇偶都包括的,偶回文子串怎么求?

在字符之间加一些插板就\(OK\)了。

例如:把abababa变成#a#b#a#b#a#b#a#

可以发现答案就是最大的\(r(i)-1\),画一下样例应该就明白了。

建议可以把再在开头和末尾加上两个不相同的字符,例如:#a#b#c#变成@#a#b#c#&,这样就不用判断边界了。

代码实现

例题:P3805 【模板】manacher算法

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int MAXN=11000005;

int n;
char s[MAXN],t[MAXN<<1];

int ans,r[MAXN<<1];
void manacher() { // 开始拉车 
    int mid=1,R=1;
    for(int i=2;i<=n;i++) {
        // 优化 
        if(i<=R) r[i]=min(r[mid*2-i],R-i+1);
        else r[i]=1;
        while(t[i+r[i]]==t[i-r[i]]) r[i]++; // 扩展 
        if(R<i+r[i]-1) mid=i,R=i+r[i]-1; // 更新 
        ans=max(ans,r[i]);
    }
}

int main() {
    scanf("%s",s+1),n=strlen(s+1);
    // 插一些字符 
    for(int i=1;i<=n;i++) t[i<<1]=s[i],t[(i<<1)-1]='#';
    t[n=n<<1|1]='#',t[0]='&',t[n+1]='@';
    manacher();
    printf("%d\n",ans-1);
    return 0;
}

当然也可以写简单点把\(if\)去掉

void manacher() {
    int mid=1,R=2;
    for(int i=2;i<=n;i++) {
        r[i]=min(r[mid*2-i],R-i);
        while(t[i+r[i]]==t[i-r[i]]) r[i]++;
        if(R<i+r[i]) mid=i,R=i+r[i];
        ans=max(ans,r[i]);
    }
}

练习题:

回文自动机\((PAM)\)

未完待续......

猜你喜欢

转载自www.cnblogs.com/smsylby/p/12216767.html
今日推荐