可以证明这个算法时间复杂度为(O(n))
至于输出长度的问题,我们记录的是向外扩展的最大距离,注意包括对称轴。对于(a|b|c|d|e)
有下面两种情况:
(1.)对称轴在分隔符上,这时候长度为一边的字母数(*2+1),因为开头结尾也加了分隔符,扩展一定最远一定在分隔符上,扩展到一边的字母数也就是((len-1)/2),两边总的长度是(len-1)
(2.)对称轴在字母上,这时长度为一边的字母数(*2),扩展到一边的字母数是(len/2),再乘(2)发现中间字母多算一遍,所以长度也是(len-1)
注意一下几点
(1.)初始扩展距离赋成(1)
(2.)开头结尾的两边再加不同的字符,避免数组越界
比如(*|a|b|c|d|e|f||)
更多非常巧妙的细节看代码
(Code)
#include<cstring>
#include<cmath>
#include<iostream>
#define maxn 30001000
using namespace std;
int n,hw[maxn],ans;
char a[maxn],s[maxn<<1];//数组一定开到两倍以上,两倍不够
void manacher()
{
int maxright=0,mid;
for(int i=1;i<n;++i)
{
if(i<maxright)//如果在已知范围内
hw[i]=min(hw[(mid<<1)-i],hw[mid]+mid-i);//mid<<1-i是中点公式,返回对称点位置,hw[mid]+mid-i=maxright-i+1
//两种情况
else
hw[i]=1;//未知则只能初始值为1
for(;s[i+hw[i]]==s[i-hw[i]];++hw[i])//这里比较巧妙,hw一开始就是1,相当于看下一个符不符合,i+hw[i]就是当前的边界+1,当不符合后最右边为i+hw[i]-1,这时候hw[i]就是扩展长度
if(hw[i]+i>maxright)
{
maxright=hw[i]+i;
mid=i;//两个都更新
}
}
}
void change()
{
s[0]=s[1]='#';//开头之前的字符是#,结尾之后无字符,数组不会越界
for(int i=0;i<n;++i)
{
s[i*2+2]=a[i];
s[i*2+3]='#';
}
n=n*2+2;//结尾后面
s[n]=0;//就是没有字符
}
int main()
{
scanf("%s",a);
n=strlen(a);
change();
manacher();
ans=1;//最短一定是1,赋初始值
for(int i=0;i<n;++i)
ans=max(ans,hw[i]);
printf("%d",ans-1);//为什么是-1推过了
}
(Update:10.28)
下面是解释答案为什么是(hw-1)的新版本,更容易理解一些
设原字符串总长度为(len)
假设长度为奇数,那么这个字符串是长成这样的
(*a*a*a*a*a*)
( herefore lceilfrac{len}{2} ceil*2=hw)
( herefore frac{len+1}{2} *2=hw)
( herefore len=hw-1)
假设长度为偶数,那么这个字符串是长成这个样子的
(*a*a*a*a*)
( herefore frac{len}{2}*2+1=hw)
( herefore len=hw-1)
证毕
(manacher)求回文串个数
(manacher)算法还可以求所有回文字串的个数,本质不同的回文字串指的是对称中心不同,因此以(i)为中心的回文字串的个数是就是原字符串向右扩展的距离
设原字符串总长度为(len)
假设长度为奇数,那么这个字符串是长成这样的
(*a*a*a*a*a*)
( herefore lceilfrac{len}{2} ceil*2=hw)
( herefore frac{len+1}{2} *2=hw)
( herefore len=hw-1)
假设长度为偶数,那么这个字符串是长成这个样子的
(*a*a*a*a*)
( herefore frac{len}{2}*2+1=hw)
( herefore len=hw-1)
证毕
假设以(i)为对称轴的最长回文串长度为奇数,因为一个字符也是回文串,所以以(i)为对称轴的回文串个数为(lceil frac{len}{2} ceil)就是(frac{len+1}{2}=frac{hw}{2})
假设以(i)为对称轴的最长回文串长度为偶数,则个数为(frac{len}{2}=frac{hw-1}{2})
又因为(hw)是奇数,所以(frac{len}{2}=frac{hw}{2})
综上,统计回文串个数只需要最后扫描加这样一句话就行
for(int i=0;i<n;++i)
ans+=hw[i]/2;