Manacher’s algorithm

LeetCode上一道求回文字符串的问题,通常容易想到的是暴力和动态规划的方法,时间复杂度为O(n^2),后来看到有一种更巧妙的算法,Manacher’s algorithm,时间复杂度为O(n)。参考大神的博客,记下来自己的理解,方便日后查阅。

1.预处理

首先,由于通常情况下,对于奇数长度和偶数长度的不同字符串,我们需要分别处理。这里,通过巧妙的预处理,在每一个字符两边增加一个特殊字符,例如:#a#b#a#a#b#a#a#b#,这样就可以枚举到每一个中心了,就是说把两种情况都统一起来。算法的思想是利用前面已经求得的回文子串的信息,来求以当前字符中心的回文子串。

2.空间换时间

然后,我们需要用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),比如S和P的对应关系:

S  #  1  #  2  #  2  #  1  #  2  #  3  #  2  #  1  #
P  1  2  1  2  5  2  1  4  1  2  1  6  1  2  1  2  1
(p.s. 可以看出,P[i]-1正好是原字符串中回文串的总长度)
那么怎么计算P[i]呢?该算法增加两个辅助变量(其实一个就够了,两个更清晰)id和mx,
其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。
注意:在算法中,id和mx是一直更新的,其表示,以id为起点,半径为mx势力范围内能够罩住的区间,即[id - mx,id + mx]。


3.算法逻辑分析

假设现在我们要求P[i],i关于id的对称点j是否有信息可以利用呢?主要看id为中心的回文串半径与P[j]的关系

总的来说,有三种情况

a.完全覆盖,不包括边界


Manacher’s algorithm_第1张图片

此时可以完全利用i的对称点j的已有信息,即P[i]=P[j];此时,不会更新id和mx值,即可以直接处理下一个字符。

b.恰好覆盖,到达边界


此时,就需要就在边界的下一个开始继续比较下去

c.未能覆盖

Manacher’s algorithm_第2张图片

此时,p[i] = mx - i 且 不会更新id和mx值,即可以直接处理下一个字符。

那么P[i]=j+P[j]-i ;因为如下图,1、13不在pj里面,就是说s[1]!=s[13],而在p4里面有s[1]=s[7],所以s[13]!=s[7],所以不能形成更长的回文串。


参考资料:

1.http://blog.csdn.net/insistgogo/article/details/12287805

2.http://sleeping1346.blog.163.com/blog/static/1746069002013622115614829/


你可能感兴趣的:(C++)