问题
求一个字符串最大的回文子串长度
例如:abc12321de,
最大回文子串:12321
返回5
小概念:
- 子串(连续)
- 子数组(连续)
- 子序列(可以连续)
笨办法:
回文分为奇回文和偶回文
奇回文:121
偶回文:1221
针对奇回文,可以从每个位置开始往左右两边扩,即可找到最大回文子串
那偶回文呢?
往里面填充特殊字符,方法:
a121b ——> #a#1#2#1#b#
找到2所在位置,长度7/2=3,即最大回文子串长度为3
a1221b——> #a#1#2#2#1#b#
找到 中间那个#所在位置,两边扩,最大长度9/2=4,即最大回文子串长度为4
注:填充的特殊字符可以是任意的字符,数组中存在的字符也可以,因为他永远是和自己比较的,不会影响计算结果。
总结下:
笨办法就是往里面填充特殊字符后,每个字符都不断去往左右两边扩,找到最长的回文子串,那么如何分析时间复杂度呢?
假设,针对这样一个字符串
#1#1#1#1#1#1#1#1#
每个位置的字符都要扩到边界去,那么
1+2+3+…+n/2+(n/2-1)+...+2+1 = O(n^2)
说明时间复杂度为 O(n^2)
- Manacher
可以将时间复杂度将为 O(n)
首先,有几个概念:
回文范围,回文直径,回文半径
最右回文边界
#1#2#1#
用 R 记录最右回文边界
用 C 记录第一次取得 R 时的位置
假设已经知道了目前的最右回文边界 R,其所对应的位置为 C,与之相对应的左边界为 L,每个位置的最右回文边界 R 都记录在一个数组pArr[]中。然后我们目前在位置 i 上,有以下两大类,四小类种情况:
- i位置上的字符在最有回文边界 R的外面(右边),那么直接暴力扩
- i 位置在R 左边,假设以 C 为中心,与 i 相对应的位置 i’,那么 i’的最右回文必然已经知道了,那么可以根据 i’的最大回文子串的范围和 L 的关系来看:
- i’的最大回文子串范围在 L 内,那么不用扩,i 位置的最大回文子串长度必然等于 i’的最大回文子串长度
- i’的最大回文子串范围有一部分在 L 之外,那么也不用扩,i位置的最大回文子串长度等于R-i
- 刚好压线,此时才需要往左右两边开始扩,从哪里开始扩呢,从 R 位置后面开始。
最后取出数组pArr中最大值
max = Math.max(max, pArr[i]);
那为何又有最有一行呢?
return max - 1;
首先,假设最后答案是 x,填充特殊字符后,最大回文直径为 d=(2*x+1),最大回文半径为r=(d+1)/2=x+1
pArr中记录的就是最大回文半径,所以最后答案x=r-1.
最后附上我的草稿笔记