算法一:枚举开头,枚举结尾,得到一个串,再扫一遍判断是否为回文串,若是,答案加
1
1
1,时间复杂度
O
(
n
3
)
O(n^3)
O(n3)。
算法二:枚举开头,枚举结尾,得到一个串,用字符串哈希判断其是否为回文串,若是,答案加
1
1
1,时间复杂度
O
(
n
2
)
O(n^2)
O(n2)。
算法三(对下文有用):枚举回文串的对称中心,分长度为奇或偶两种情况,分别向两端延伸,若延伸到的最大长度为
s
s
s(以奇数的情况为例,最大扩展到
s
t
i
−
s
+
1
st_{i-s+1}
sti−s+1和
s
t
i
+
s
−
1
st_{i+s-1}
sti+s−1),那么答案加上
s
s
s,时间复杂度
O
(
n
2
)
O(n^2)
O(n2)。
枚举回文串的对称中心,分长度为奇或偶两种情况,分别二分能延伸的长度,用字符串哈希判断,若延伸到的最大长度为
s
s
s(以奇数的情况为例,最大扩展到
s
t
i
−
s
+
1
.
.
s
t
i
+
s
−
1
st_{i-s+1}..st_{i+s-1}
sti−s+1..sti+s−1),那么答案加上
s
s
s,时间复杂度
O
(
n
log
2
n
)
O(nlog_2 n)
O(nlog2n)。
提出疑问
有没有更快的方法,比如
O
(
n
)
O(n)
O(n)?
有没有更简洁的方法,不用哈希?
答案是:有!
算法介绍
算法名称:Manacher’s Algorithm
主要思路是用已经计算出的回文子串,利用它回文的特性,对即将计算的回文子串提供一定的帮助。
需要记录
l
,
r
l,r
l,r表示已经找出的回文子串中,右端点最靠右的回文子串所在区间,
初始值分别为
0
,
−
1
0,-1
0,−1。
以下以长度为奇数的情况为例,介绍算法操作流程。
从
1
−
n
1-n
1−n依次枚举字符串的对称中心,对于每个
i
i
i,考虑向两边扩展,扩展的最长长度为
d
i
d_i
di(也就是最大扩展到
s
t
i
−
d
i
+
1
.
.
s
t
i
+
d
i
−
1
st_{i-d_i+1}..st_{i+d_i-1}
sti−di+1..sti+di−1)。
定义朴素算法为上文“用已有的知识”中的算法三,
那么我们考虑优化这个朴素算法,算法的核心是利用已有的
d
d
d来加速计算当前的
d
i
d_i
di。
分两种情况:
一、
i
>
r
i>r
i>r(若长度为偶数则
i
≥
r
i≥r
i≥r)
直接调用朴素算法计算
d
i
d_i
di。
二、
i
≤
r
i≤r
i≤r(若长度为偶数则
i
<
r
i<r
i<r)
即说明
i
i
i在回文子串
s
t
l
.
.
s
t
r
st_l..st_r
stl..str内,那么找到
i
i
i在这个回文子串中对称的点
j
j
j,
j
=
l
+
r
−
i
j=l+r-i
j=l+r−i
因为对称,所以巧妙地利用这个性质,
d
i
=
d
j
d_i=d_j
di=dj,
思考:这样是正确的吗??
其实不然——
因为以为中心
j
j
j的回文子串,左边界可能超出
l
l
l,那么对称过去到了
i
i
i就不在大回文子串
s
t
l
.
.
s
t
r
st_l..st_r
stl..str内了,不能保证也回文,所以
d
i
=
m
i
n
(
d
j
,
r
−
i
+
1
)
d_i=min(d_j,r-i+1)
di=min(dj,r−i+1)
同理,对
i
i
i而言,以它为中心的实际最大回文子串可能右边界超出
r
r
r,那么
d
j
d_j
dj是计算不到的,所以,在上式的基础上,再次调用朴素算法。