题解:P3805 【模板】manacher

· · 题解

题解

【模板】manacher 算法

算法简介

Manacher 算法,又称为马拉车算法,是一种用于查找字符串中最长回文子串的高效算法。它由 Manacher 在 1975 年提出,能够在 O(N) 的时间复杂度和空间复杂度内解决问题。

算法思想

算法的核心在于通过特殊字符的插入,将偶数长度和奇数长度的回文子串统一处理。

那么,推导与实现过程如下:

首先,有一个小技巧:

对于字符串 s,用一个 s 中不存在的字符把 s 中的字符隔开:

例如 \texttt {aba} \rightarrow \texttt {¥a¥b¥a¥}

原串最长回文子串长度等于新串最长回文子串长度的一半。

PS:接下来出现的 s 若无特殊说明均为插入字符后得到的新串。

算法详解:

manacher 算法需要维护最大回文半径。

最大回文半径:从 s_i 出发能拓展的字符数,记作:p_i

我们从左向右依次计算 p_i。\ 假设要计算 p_i,则此时应已经计算了 p_1,p_2,p_3,\dots,p_{i-1}。\ 为了计算 p_i,在枚举 i 的过程中,我们要维护使得 R 最大的区间 [L,R]

其中 \begin{cases} L=M-p_M+1 & (M<i) \\ R=M+p_M-1 & (M<i) \end{cases}

接下来开始分类讨论:

![](https://cdn.luogu.com.cn/upload/image_hosting/xhupfzr4.png) $i \le R$ 时,找到 $i$ 关于 $M$ 的对称点 $k$: 若 $p_k$ 对应的回文区间不包含 $L$,令 $p_i=p_{2M-i}$。 ![](https://cdn.luogu.com.cn/upload/image_hosting/3wnljgr1.png) 若 $p_k$ 对应的回文区间包含 $L$,令 $p_i=p_{R-i+1}$ 并继续暴力扩展。 ![](https://cdn.luogu.com.cn/upload/image_hosting/1xji6bcm.png) 再看回 $p_k$ 对应的回文区间不包含 $L$ 的情况: 若 $p_k$ 对应的回文区间不包含 $L$,则 $L<k-p_k+1$,又 $L=2M-R$ 且 $k=2M-i$,则 $(2M-R)<(2M-i)-p_{2M-i}+1$,化简得 $p_{2M-i}<R-i+1$。 然后是 $p_k$ 对应的回文区间包含 $L$ 的情况: 若 $p_k$ 对应的回文区间包含 $L$,则 $L \ge k-p_k+1$,又 $L=2M-R$ 且 $k=2M-i$,则 $(2M-R) \ge (2M-i)-p_{2M-i}+1$,化简得 $p_{2M-i} \ge R-i+1$。 所以经过观察总结可得 $i>R$ 时,令 $p_i=1$,并暴力向两边扩展。 $i \le R$ 时,令 $p_i = \min (p_{2M-i},R-i+1)$,并暴力向两边扩展。 ## 正确性证明 时间复杂度为何是 $O(N)$ 呢? 因为每次在更新右端点时,$i+p_i$ 必然会超过原来的且小于 $n$,即呈递增趋势,不会重复更新。所以最多只会更新 $O(N)$ 次,那么总的时间复杂度就是 $O(N)$ 了。 ## Code 接下来就是最美的代码实现了!!! 算法部分: ```cpp int manacher(string s) { int M=0,R=0; for(int i=1;i<=n;i++) { if(i>R) p[i]=1; else p[i]=min(p[2*M-i],R-i+1);//分类讨论 while(i-p[i]>=1&&i+p[i]<=n&&s[i-p[i]]==s[i+p[i]]) p[i]++;//暴力拓展 if(i+p[i]-1>R) M=i,R=i+p[i]-1;//更新 } int ans=0; for(int i=1;i<=n;i++) ans=max(ans,p[i]); return ans-1; } ``` 本题代码 ```cpp #include<bits/stdc++.h> #define int long long using namespace std; string s,S; int p[30000005],n; int manacher(string s) { int M=0,R=0; for(int i=1;i<=n;i++) { if(i>R) p[i]=1; else p[i]=min(p[2*M-i],R-i+1); while(i-p[i]>=1&&i+p[i]<=n&&s[i-p[i]]==s[i+p[i]]) p[i]++; if(i+p[i]-1>R) M=i,R=i+p[i]-1; } int ans=0; for(int i=1;i<=n;i++) ans=max(ans,p[i]); return ans-1; } signed main() { cin>>S; s+=' '; for(int i=0;i<S.size();i++) s+='$',s+=S[i]; s+='$'; n=s.size()-1; cout<<manacher(s); return 0; } ```