每天学一丢意为每天学一点丢一点。
Manacher
M
a
n
a
c
h
e
r
Manacher
Manacher 算法主要解决的问题是最长回文子串,为了解决奇偶性的问题,我们在原字符串中加入了新的字符作为通配符,例如我们将
a
b
a
b
a
b
c
abababc
abababc 扩充为了
#
a
#
b
#
a
#
b
#
a
#
b
#
c
#
\#a\#b\#a\#b\#a\#b\#c\#
#a#b#a#b#a#b#c#,因为
#
\#
# 号都是相同的,所以它并不影响匹配关系,但是我们在考虑对称轴的时候,却可以把原本位于两个字符中间的对称轴,变为一个实际存在的字符
#
\#
# 上,这样便于之后的讨论。
在将字符串扩充之后,我们引入了两个变量,
m
a
x
r
maxr
maxr 和
p
o
s
pos
pos,分别表示当前匹配回文串的最后端点,以及这个回文串的中点。
我们顺序访问扩充后的串,对于访问到的节点
i
i
i,它一定在
p
o
s
pos
pos 的右侧(
p
o
s
pos
pos 是已匹配回文串的对称轴):
如果
i
i
i 在
m
a
x
r
maxr
maxr 左侧,则我们可以找到一个关于pos对称的位置
j
j
j,他们已匹配的回文部分应该是相同的。但是这部分回文部分,我们只能保证在
m
a
x
r
maxr
maxr 以内的是确定回文的。然后需要再向左右扩展
如果
i
i
i 在
m
a
x
r
maxr
maxr 右侧,则我们只能知道长度为1的回文子串,即它本身,之后需要对其分别向左右扩展。
那么基本步骤是:
s
t
e
p
1
:
 
i
step1:\, i
step1:i 在
m
a
x
r
maxr
maxr 的左侧
p
[
i
]
=
min
(
p
[
2
∗
p
o
s
−
i
]
,
r
−
i
)
p[i] = \min\left(p\left[2*pos-i\right], r-i\right)
p[i]=min(p[2∗pos−i],r−i),不然
p
[
i
]
=
1
p[i] = 1
p[i]=1
s
t
e
p
2
:
 
step2:\,
step2: 对
p
[
i
]
p[i]
p[i]进行扩展
s
t
e
p
3
:
 
step3:\,
step3: 更新
p
o
s
pos
pos 和
m
a
x
r
maxr
maxr
代码
int p[maxn<<1]; //p[i]-1是以i为中点的回文串长度
char s[maxn], now[maxn<<1]; // now is #a#b#c#
int k;
void Manacher(){
int lens = strlen(s);
now[0] = '#';
rep(i, 0, lens){
now[i<<i] = '#';
now[i<<i|1] = s[i];
}
now[lens<<1] = '#';
now[lens<<1|1] = '\0';
int len = (lens<<1|1);
int pos = 0, maxr = 0;
rep(i, 0, len){
if(i < maxr) p[i] = min(p[2*pos-i], maxr-i);
else p[i] = 1;
while(i-p[i]>=0 && i+p[i]<len && now[i-p[i]] == now[i+p[i]])
p[i]++;
if(i+p[i] > maxr){
pos = i;
maxr = i+p[i];
}
}
}