怎么判断一个字符串的最长回文子串是否在头尾_求解最长回文子串:马拉车(manacher)算法详解...

这个算法是在做一道“求最长回文子串”的题目时查到的一个算法,在此之前的方法是遍历所给字符串,使用中心展开的方法求的最长的子串

def 

上面的中展开求长度的用时,加上遍历字符串的循环,时间复杂度为

,而使用“马拉车”算法可以做到线性时间复杂度,在解释该算法之前,先说一下中心展开算法的主要问题

这个算法最大的一个问题就是重复计算,没有充分利用回文字符串的特性——对称性,所以改进的关键就在于减少这种不必要的展开,充分利用对称性,不多说了,先上栗子

现给定一个字符串T:#b#a#b#c#b#a#b#c#b#a#x

为什么会有这么多‘#’,后面会解释,这里直接看做字符串本身的一部分,另外最后那个x并不是字母‘x’,而是指代未知字符,后面会详细说明

另外在设置一个与字符串等长的数组p,存储字符串中对应字符的“对称半径(不包含自身)”,例如p[0]=0,p[1]=1,p[2]=0,所以将数组p中所有的元素都算出来后,自然就得到最大回文子串的信息了,计算方法如下图:

176b5636b1daea66176df98fb97791cf.png

上图中第一排是索引,从0开始,第二排是就是字符串T对应索引的字符(废话),第三排是该字符的对称半径

假设现在对计算到p[11] = 9了,这个是已知的,设该点为c,即c=11,也就是上图中标黄的a的位置,上图标绿的就是它的对称半径的边界

另外,标红的就是之前T里面的未知字符x,对算法而言,现在除了知道它不等于‘b’(等于b的话p[11]就不是9了)之外其余一概不知,再往后的字符就更不知道了,所以是‘?’表示

接下来,就是要进一步计算p中剩下的值了,现在轮到p[12]了,首先,找到i=12关于c的对称点,即10,设为j,关系如下

j = 2 * c - i

然后检查p[j](即p[10])的对称半径是否在T[c](标黄的a)的对称半径范围之内,观察上图可知在c的覆盖范围之内,因为p[j] = 0,所以p[i]也等于0

理解这一点很关键,因为以a为中心的左右9个字符都是对称的,所以p[i] = p[j],如果不等于,就破坏了T[c]的对称性,如果还是不理解可以再看一个例子

b1b74a0295c2abac712a9bae3a65dccd.png

i=13,所以j=9,p[j]=1,其覆盖范围如图中灰色部分,同样没有超过c对称半径的边界(标绿的),所以p[i] = p[j] = 1,为什么?因为以c为中心,左边9个字符和右边9个字符是对称的,所以他们的对称性(在不超过c的边界之前)是一样的

if 

上面的mx表示c的对称边界,即mx = c + p[c]

根据同样的方法可以轻松计算出p[14] = 0,接下来是p[15],如图:

4021cab0b345f2c7e404fdeb31dec0e4.png

可以看到j = 7, p[j] = 7,其对称范围已经超过c的对称边界了,因为c的对称性约束,可以知道p[i] = mx - i = 5

可能有人会有疑问,为什么是5,不是7?可以看到,

,如果p[i]要等于7,甚至等6或者更大,就违背了这个约束条件,也就是说需要T[21]=b

但是这样一来,p[c]就不等于9了,想不明白的可以往上看看

是如何得来的
if 

再来看另外一个栗子:

9fd94a218822814af05c2c2e56d46085.png

此栗中,c=5,i=7,j=3,可以看到,p[j]=1,其对称半径正好卡在c的边界上,这时候p[i]等于多少?只能说至少等于1,再上一个图说明原因:

c6fc0e663b74b4a65f111b7110a064a4.png

可以看到,该图和之前那个图并不矛盾,之前T[9]只检查到不等于d(因为p[c]=3的约束),但是可以等于其他的字符啊,例如‘c’,包括后面的字符,也是未知的,需要进一步去判断,所以,这种情况除了使用中心展开法挨个对比就没有更好的办法了

j 

上面的max_id和max_radius是记录的最大对称半径和其索引

上面的算法,充分的利用了回文字符串的对称特性,避免了重复计算,其实上面的算法也就是马拉车算法的主要步骤,当然,还有一种情况没有说明,比如i > mx的情况(例如刚开始对字符串进行遍历时),这种情况也是直接用中心展开求得半径

还有就是上面说的‘#’字符问题,这个是为了避免因为所给字符串的长度不同(奇数或偶数),统一在字符串各字符之间以及收尾插入一个‘#’号,这样无论所给字符串是奇数个还是偶数个字符,都能调整为奇数

下面,贴上完整代码:

def 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值