【模板】manacher算法
题目描述
给出一个只由小写英文字符a,b,c,…y,z\texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt za,b,c,…y,z 组成的字符串SSS,求SSS中最长回文串的长度 。
字符串长度为nnn。
输入格式
一行小写英文字符a,b,c,…y,z\texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt za,b,c,…y,z组成的字符串SSS。
输出格式
一个整数表示答案。
输入输出样例
输入 #1
aaa
输出 #1
3
说明/提示
1≤n≤1.1×1071\le n\le 1.1\times 10^71≤n≤1.1×107。
思路
为了在接下来的测试里不被字符串坑,所以不得不来学一下Manacher算法。
Manacher算法可以做到在O(n)O(n)O(n)的时间复杂度内实现最大回文子串的查找。
在Manacher算法中,首先要在原字符串的头、尾以及每两个字符的中间插入一个特殊字符。
例如,字符串SSS:
a,b,c,b,a,d,a\texttt a,\texttt b,\texttt c,\texttt b,\texttt a,\texttt d,\texttt aa,b,c,b,a,d,a
我们可以按照上述方法,构造出字符串S′S'S′:
*,a,*,b,*,c,*,b,*,a,*,d,*,a,*,\texttt *,\texttt a,\texttt *,\texttt b,\texttt *,\texttt c,\texttt *,\texttt b,\texttt *,\texttt a,\texttt *,\texttt d,\texttt *,\texttt a,\texttt *,*,a,*,b,*,c,*,b,*,a,*,d,*,a,*,
这样,我们就把所有的回文子串都变成了奇回文(中点坐标是整数的回文子串)。
我们设三个变量r,m,maxnr,m,maxnr,m,maxn,分别表示目前查到的最大回文子串的右端点,目前查到的最大回文子串的中点,目前查到的最大回文子串的半径,再设pip_ipi表示以iii为中点的最大回文子串的半径。
然后,我们开始扫描整个字符串S′S'S′。
对于每个字符S′i{S'}_iS′i,如果i>ri>ri>r,那么已知的以iii为中点的最大回文子串就只能是iii自己,即pi=1p_i=1pi=1;否则,已知的以iii为中点的最大回文子串的半径要么等于p2∗m−ip_{2*m-i}p2∗m−i(这意味着以iii为中点的最大回文子串中的每个字符S′j{S'}_jS′j,满足∀j∈[m,r]\forall j \in [m,r]∀j∈[m,r]且i∈[m+r2,r]i \in [\frac{m+r}{2},r]i∈[2m+r,r]),要么等于r−i+1r-i+1r−i+1(这意味着以iii为中点的最大回文子串中的每个字符S′j{S'}_jS′j,满足∀j∈[m,r]\forall j \in [m,r]∀j∈[m,r]且i∈[m,m+r2]i \in [m,\frac{m+r}{2}]i∈[m,2m+r]),在这里,我们取两种情况的最小值,即pi=min(p2∗m−i,r−i+1)p_i=min(p_{2*m-i},r-i+1)pi=min(p2∗m−i,r−i+1)。
接着,我们对已知的pip_ipi,把它向外扩展,看看能不能构造出半径更长的以它为中点的最大回文子串。因为有了上一步的处理,在这一步中,我们不用向外扩展太多就可以找到半径最长的以它为中点的最大回文子串。这就使得算法的效率极大地提高了!
得到了pip_ipi的最大值之后,我们就可以更新r,c,maxnr,c,maxnr,c,maxn的值了。
最后的答案就是maxn−1maxn-1maxn−1。(证明:在字符串S′S'S′中,它的最大回文子串的一半的是*,a,*,b,*,c,*\texttt *,\texttt a,\texttt *,\texttt b,\texttt *,\texttt c,\texttt **,a,*,b,*,c,*,显而易见,该子串删去一个*\texttt **之后,在剩余的*\texttt **的位置填上字母,构造的字符串经过排序后就是原字符串SSS的一个子串,而且是SSS的最大回文子串)
代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
int cnt,r,m,p[22000001],len,maxn,id;
char ss[22000001];
string s;
int main()
{
cin>>s;
len=s.size()*2+1;
for(int i=1;i<=len;i++)
{
if(i&1)
ss[i]='#';
else
ss[i]=s[id++];
}
r=m=-1;
for(int i=1;i<=len;i++)
{
if(i>r)
p[i]=1;
else
p[i]=min(p[2*m-i],r-i+1);
while(i+p[i]<=len&&i-p[i]>=1)
{
if(ss[i+p[i]]==ss[i-p[i]])
p[i]++;
else
break;
}
if(i+p[i]>r)
{
r=i+p[i]-1;
m=i;
}
maxn=max(maxn,p[i]);
}
printf("%d",maxn-1);
return 0;
}

本文详细介绍了Manacher算法,这是一种用于寻找给定字符串中最长回文子串的高效算法,具有O(n)时间复杂度。通过在原字符串两端插入特殊字符,将所有回文子串转换为奇回文,然后利用已知信息动态扩展回文子串,逐步更新最大回文子串的半径。文章还给出了算法的C++实现代码,帮助读者理解并应用Manacher算法。
327

被折叠的 条评论
为什么被折叠?



