给出一个只由小写英文字符a,b,c...y,z
组成的字符串
,求
中最长回文串的长度(见洛谷P3805
)。
我们先考虑暴力算法:枚举一个回文中心,然后向两边扩展。
这样做有两个问题:一是需要区分奇数和偶数。比如aba
和abba
,我们肉眼一看就知道两个都是回文,但是对于偶数回文串而言,它们的回文中心不是一个可见字符(比如abba
的回文中心在两个b
之间)。二是时间复杂度太高。枚举的时间复杂度为
(
表示字符串
的长度)。而拓展的时间复杂度最坏为
,总的时间复杂度为
。
,是解决此问题的
算法。首先,为了统一奇偶情况,我们在每个字符之间加上一个没有在
中出现的字符(比如aba
变成$a$b$a$
)。
记 表示以第 个字符为回文中心的最长回文半径, 为最长回文右边界, 为最长回文右边界中心, 表示当前求解 。
考虑如何求 数组,求解有好几种情况:
- 当 在 左边且在 右边时, 关于 的对称点为 ,我们令 ,然后从 开始拓展求解。
- 当 在 右边时,我们就按原始方法进行计算。
const int N=11001000;
char s[N],s1[N<<1];
int p[N<<1],ans,n;
inline void manacher(){
s1[0]='~';s1[1]='$';
for(int i=1;i<=n;i++){
s1[i*2]=s[i];
s1[i*2+1]='$';
}
}
inline void calc_answer(){
for(int t=1,r=0,mid=0;t<=2*n+1;t++){
if (t<=r) p[t]=min(p[mid*2-t],r-t+1);
while (s1[t-p[t]]==s1[t+p[t]]) ++p[t];
if (p[t]+t>r) r=p[t]+t-1,mid=t;
if (p[t]>ans) ans=p[t];
}
}
int main(){
// freopen("t1.in","r",stdin);
scanf("%s",s+1);
n=strlen(s+1);
manacher();
calc_answer();
printf("%d",ans-1);
return 0;
}