manacher
void manacher(char s[],int n,int p[])
{
int l=0,r=0;
for(int i=0;i<n;++i)
{
if(r<i)l=r=i;
p[i]=min(r-i,p[l+r-i]);
while(i-p[i]>=0&&i+p[i]<n&&s[i-p[i]]==s[i+p[i]])++p[i];
if(i+p[i]>r)r=i+p[i],l=i-p[i];
if(p[i]-1>ans)ans=p[i]-1;
}
}
分析:
- 一般用 manacher 解决问题的时候,我们在每个字符之间插入一个‘#’
比如对于字符串
记原串为
p[i] 表示从i 点出发,往一个单方向最长的长度,使得这个串是回文串。
说起来有点绕,举个例子,对于串
-
这里的
l,r 是开区间,表示一个回文串的区间。 -
为什么
\tt p[i]=min(r-i,p[l+r-i]) ?
对于
-
注意一定要判边界,不要数组下标越界(
\tt i-p[i]>=0\&\&i+p[i]<n ) -
由于我们跑 manacher 的串是插入了'#'的,所以
p[i]-1 即为原串中回文串的长度
如果
为什么
不妨分类讨论:
- 如果
T_i 是一个字母,那p[i] 对应的那一段字符串应该是:
或
(这里的'X'表示某个字母)
根据刚才的结论,因为
显然,‘X’的数量与‘#’的数量相同,所以此时的
由于这个
- 如果
T_i 是'#',那p[i] 对应那一段字符串即为:\tt \#X\#X……\#X\# 或
\tt \#X\#……X\#X\# (这里的'X'表示某个字母)
此时,对应的原串是一个长度为偶数的回文字符串,记这个回文字符串为
显然,此时'#'的数量比'X'的数量多了1,又因为这是一个长度为偶数的回文串,所以
由于
所以
- 如果题目中要让我们求长度为奇数的回文字符串的话,那么我们不用插入'#',直接跑 manacher 就行了