manacher一般是用来处理回文子串的,通过一些处理将时间复杂度从
O
(
n
3
)
O(n^3)
O(n3)或者
O
(
n
2
)
O(n^2)
O(n2)降至
O
(
n
)
O(n)
O(n)。
我们都知道,找奇数长度的回文串是容易的,只需要确定中间的字符并往两边扩展即可。
于是就可以在原字符串的两个字符之间插入一个原字符串中不可能出现的字符,例如:abcba→#a#b#c#b#a#,abba→#a#b#b#a#,长度就变成了2l+1。
那么用p[i]存储以i为中心的最长回文串的半径(理解成包括中间字符或者是保证回文的最远一位的右边一个位置),可以发现p[i]-1就是以i为中心的在原串中的最长回文子串的长度。
接下来的问题就成了如何求p[i]。
假设我们找到了一个以
i
d
id
id为中心,最远能延伸到
R
=
i
d
+
p
[
i
d
]
R =id+p[id]
R=id+p[id] 的回文串
s
0
s_0
s0(再说一遍,R上并不回文,回文的范围是
[
i
d
−
(
p
[
i
d
]
−
1
)
,
i
d
+
(
p
[
i
d
]
−
1
)
]
[id-(p[id]-1),id+(p[id]-1)]
[id−(p[id]−1),id+(p[id]−1)]),需要处理
p
[
i
]
p[i]
p[i]。
如果
i
<
R
i<R
i<R,则
i
i
i在
s
0
s_0
s0内,
i
i
i关于id的对称点
j
=
2
∗
i
d
−
i
j=2*id-i
j=2∗id−i也在之内。以
j
j
j为中心且包含在
s
0
s_0
s0的回文串关于
i
d
id
id对称后仍是回文串,即
p
[
i
]
p[i]
p[i]不大于
p
[
j
]
p[j]
p[j],但是以j为中心的回文串在
s
0
s_0
s0之外的部分对称后不能保证在
i
i
i上回文,即
p
[
i
]
p[i]
p[i]不大于
R
−
i
R-i
R−i,故
p
[
i
]
=
m
i
n
(
i
d
+
p
[
i
d
]
−
i
,
p
[
2
∗
i
d
−
i
]
)
p[i]=min(id+p[id]-i,p[2*id-i])
p[i]=min(id+p[id]−i,p[2∗id−i])。
如果
i
>
R
i>R
i>R,我们就不能利用
i
i
i关于
i
d
id
id的对称点,需要暴力计算,并维护id。
Code:
代码内容和上述内容不太相同,例如对p[i]的处理和暴力修改以及id的维护是对每一个i都进行的,而上述R是每次取i+p[i]最大。
这是我2年前的提交记录(
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 110005
using namespace std;
char s[maxn*2],t[maxn];
int p[maxn*2];
int main()
{
while(~scanf("%s",t))
{
int len=strlen(t),n=len*2+2,ans=0,id=0;
memset(s,0,sizeof(s));
for(int i=0;i<len;i++) s[i*2+2]=t[i];
s[0]=1,s[n]=2;
for(int i=1;i<n;i++)
{
if(p[id]+id>i) p[i]=min(p[2*id-i],p[id]+id-i);
else p[i]=1;
while(s[i-p[i]]==s[i+p[i]]) p[i]++;
if(id+p[id]<i+p[i]) id=i;
if(ans<p[i]-1) ans=p[i]-1;
}
printf("%d\n",ans);
}
}
本来以为自己不会写了结果计概B的上机作业把我炸出来了
计概B出一个找1e6的最长回文子串就离谱
算是补上了2年前就立过的一个flag吧…