算法之Manacher算法
给定一个字符串 s,找到 s 中最长的回文子串?
回文是一个正读和反读都相同的字符串,例如,“aba” 是回文,而 “abc” 不是。由于回文分为偶回文(比如 bccb)和奇回文(比如 bcacb),而在处理奇偶问题上会比较繁琐,所以这里我们使用一个技巧,具体做法是:在字符串首尾,及各字符间各插入一个字符(前提这个字符未出现在串里)。举个例子:s=“abbahopxpo”,转换为s_new=“#a#b#b#a#h#o#p#x#p#o#”
定义一个辅助数组int p[]
,其中p[i]
表示以 i 为中心的最长回文的半径,例如:
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
s[i] | # | a | # | b | # | b | # | a | # | h | # | o | # | p | # | x | # | p | # |
p[i] | 1 | 2 | 1 | 2 | 5 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 4 | 1 | 2 | 1 |
可以看出,p[i] - 1
正好是原字符串中最长回文串的长度。p[i]
-1的最大值就是我们的最终结果。
接下来可以得到如下结论:
- 设置两个变量,mx 和 id 。mx 代表以 id 为中心的最长回文的右边界,也就是mx = id +
p[id]
。 - 如果mx > i,那么P[i] >= MIN(P[2 * id - i], mx - i)。
- 对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了。
对于第二点的分析如下:
当 mx - i > P[i] 的时候(j=2*id-i),以S[i]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[j]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j],如下。
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
s[i] | # | a | # | b | # | b | # | a | # | h | # | o | # | p | # | x | # | p | # |
p[i] | 1 | 2 | 1 | 2 | 5 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 4 | 1 | 2 | 1 |
id | mx | ||||||||||||||||||
j | i |
当 P[i] >= mx - i 的时候,以S[i]为中心的回文子串不一定完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能老老实实去匹配了。
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
s[i] | # | a | # | b | # | b | # | a | # | h | # | o | # | p | # | x | # | p | # |
p[i] | 1 | 2 | 1 | 2 | 5 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 4 | 1 | 2 | 1 |
id | mx | ||||||||||||||||||
j | i |
对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了。
对于manacher 算法:
- 时间复杂度为
- 空间复杂度为
1 | public class Manacher { |