问题提要:
回文串即从左到右与从右到左的序列完全相同的字符串,现在要寻找一个字符串中的最长回文子串
针对这个问题有一下几种解题思路
暴力求解:循环每一个子串,然后再进行子串的回文判断,但这样的话时间复杂度可以达到O(n^3)的级别
动态规划:在进行回文判断时,我们可以把这个问题分解为一个小问题, 判断 s(i, j)是否为回文,则可以在s[i] == s[j]的情况下判断s(i+1, j-1)是否为回文
// 动态方程如下
{
s(i, i) true
s(i, j) = (s[i] == s[j]) ^ s(i + 1, j - 1);
}
// 动规时间复杂度为O(n^2), 空间复杂度也达到了O(n ^2)
Manacher算法基础,中心扩展算法
中心扩展法,该思想是马拉车(Manacher)算法的基础,该算法通过遍历字符串的i作为回文中心,然后向i的两边进行扩展试探求出其是都为回文,由于受回文结束条件的影响,使用这个遍历和试探的过程为n次,故该算法的时间复杂度为O(n^2), 空间复杂度为O(1);
在中心扩展的过程中可以看到,每次i作为回文中心向两边遍历时都是从i开始重新遍历的,那么这个过程中是否像kmp算法那样利用前面遍历过的数据信息?
答案是肯定的,而Manacher算法就是利用了之前遍历过的结构来实现O(n)的线性复杂度
Manacher算法
因为回文串的回文中心有时候是一个字符有时候是以两个字符为中心,即aba,abba这两种类型,为了能统一进行处理,算法实现对字符串进行了预处理
对字符串的两边和字符间都插入一个特殊标记字符,形成
#a#b#a#
#a#b#b#a#
可以看到现在字符串都会变成奇数串,因此要么以字符作为回文中心,要么就是#特殊字符,又或者解释为原字符串中的空隙为中心
同时处理后的处理串和原串的字符下标存在一一对应关系
我们假设处理串为P, 原串为S,那么去掉P串最右边的#后那么每一个字符的左边都会跟着一个#,
因此(P.length - 1) == S.length , 那么可以得出在P串中的字符下标S[i] = (P[i] - 1)/2
得到以上的结论后,继续进行分析
对处理串P进行中心扩展时,如何利用前面已经得到的回文结果呢,假设P前面某点center的回文右端在i后面,那么根据回文对称,那么i关于那一点对称的P[2 * center - i]就会是P[i]的最小值,而为了能利用起center的回文属性,那么center的回文右端和i的关系就很重要
这个回文右端我们命名为maxRight, 表示i前面的所有回文子串中能到达的最远右端
其中i和maxRight的关系分为两种情况
i >= maxRight时就需要进行传统的中心扩展进行判断i < maxRight时,这个时候有可用根据P[2 * center - i]的值分为3种情况
①当P[2 * center - i]的回文扩展范围还是在P[center]的范围内时,那么根据回文的性质P[i] = P[2 * center - i]

根据回文性质可用得出a == d, b == c, 但既然P[2 * center - i]没有进行向下扩展,那么肯定a != b, 所以c != d, 因此此情况下P[i] = P[2 * center - i]
②若此处P[2 * center - i]正好扩展到center的边界

利用center回文的特性可用得出,b == c,同时a != d, 那么要判断P[i]的值就需要进一步判断c和d是否相等,此时应当从c和d作为端点进行中心扩展判断求出P[i]
③ P[2 * center - i]的扩展左端已经超过了center的范围

如图所示P[2 * center - i]的回文区域为黑线款选区,同时根据center的回文属性图中橙色区的字符是相等的
因为P[2 * center - i],所以a == b,又因为center,所以b == c,但同时a != d
因此此时P[i]的最大扩展范围只能是橙色区域
几种情况的分析充分的利用了回文的属性,而这也是Manacher算法的根本,具体代码可以参考
力扣题解
671

被折叠的 条评论
为什么被折叠?



