Manacher算法

Manacher 算法可在 \(O(n)\) 解决最长回文串的问题。

通过预处理 \(d_i\) 表示以 \(i\) 为中心的回文串向两边延伸的最长长度来解决原问题。

如对于字符串 abcba(下标从\(1\)开始),\(d_3=3\),即 \(cba\)

P3805【模板】manacher

算法流程如下:

改造字符串

在开头插入 $,末尾插入 !,每个字符的左边和右边都插入一个 #

例如:

abcdefg
改造为:
$#a#b#c#d#e#f#g#!

在开头和末尾插入两个不同的字符,可以避免边界问题。而在相邻字符插入 #,则是为了处理长度为偶数的回文串,例如:aa 转化为 #a#a#,即变为以第二个 # 为中心的回文串。

预处理 \(d_i\)

在预处理 \(d_i\) 时,需要维护一个表示当前右端点最右边的回文串区间,不妨用 \([l,r]\) 表示。

假设现在正在处理 \(i\),那么分为两种情况:

\(i\in [l,r]\)
对于 \(i\)\([l,r]\) 中,可以找到一些性质,记 \(i'\) 表示 \(i\) 关于 \(\frac{l+r}{2}\) 的对称点(注意:此时 \(d_{i'}\) 已经求出),那么分为以下几种情况:

\(i'\) 的回文长度未超出 \([l,r]\)(下图蓝色为回文部分)
由于 \([l,r]\) 本身为回文串,所以在对称位置,回文情况是一致的,那么直接 \(d_i=d_{i'}\) 即可。

\(i'\) 的回文长度超出 \([l,r]\)
那么我们只能确认图中(即在\([l,r]\)内的部分)蓝色部分为回文,至于红色部分(超出区间部分),我们无法确认,只能通过暴力枚举处理。

\(i>r\)
直接暴力往两边扩展即可。

计算完 \(d_i\) 记得更新区间。

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=3e7;
int n,d[N];
string s;
char ch[N];

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    cin>>s;
    ch[0]='$';//改造字符串
    for(int i=0;i<s.size();i++) {
        ch[++n]='#';
        ch[++n]=s[i];
    }
    ch[++n]='#';
    ch[++n]='!';
    d[1]=1;
    for(int l=1,r=1,i=2;i<n;i++) {
        if(i>=l&&i<=r) d[i]=min(d[l+r-i],r-i+1);//l+r-i即为i',r-i+1即i到右边界距离
        while(ch[i+d[i]]==ch[i-d[i]]) d[i]++;//暴力扩展
        if(i+d[i]-1>=r) {r=i+d[i]-1; l=i-d[i]+1;}//更新区间
    }
    int ans=0;
    for(int i=1;i<=n;i++) {
        ans=max(ans,d[i]-1);
    }
    cout<<ans;
    return 0;
}

扩展应用

裸的Manacher算法只能求出以一个字符为中心向两边延伸的最长回文长度,但是没法求出以某个点为起点的最长回文长度,需要通过递推的方式求解。

首先预处理出上文的 \(d\) 数组,然后用 \(i\)\(d_i\) 计算出 \([l_i,r_i]\),此时计算出的所有区间,都是尽可能长的区间,也就是说:如果 \([1,5]\)是回文,那么\([2,4]\)也一定是回文,而目前的做法只能处理出 \([1,5]\),处理不出 \([2,4]\)

所以考虑用 \(dp\) 解决问题,设 \(f_{i}\) 表示以 \(i\) 为开头的最长长度,先用 \(i,d_i\) 计算出一部分 \(f_{i}\)

从上文 \([1,5]\)\([2.4]\) 的例子也能得到一些启发,即前一个开头的最长回文损失两个(一头一尾)就变成当前位置回文长度,即:

\[f_i\leftarrow f_{i-1}-2 \]

当然,在 Manacher 算法中,由于字符之间用 \(#\) 隔开,所以实际改写为:

\[f_i\leftarrow f_{i-2}-2 \]

类似例题:P4555 [国家集训队] 最长双回文串
提交记录:记录详情

posted @ 2024-06-29 20:49  2017BeiJiang  阅读(1)  评论(0编辑  收藏  举报