题目描述:
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
分析:
这道题有很多解法,例如暴力匹配、中心扩散、动态规划等,这里我使用了一个很重要的算法思想—— Manacher 算法。
[Manacher(1975)] 发现了一种线性时间算法,可以在列出给定字符串中从字符串头部开始的所有回文。并且,Apostolico, Breslauer & Galil (1995) 发现,同样的算法也可以在任意位置查找全部最大回文子串,并且时间复杂度是线性的。因此,他们提供了一种时间复杂度为线性的最长回文子串解法。替代性的线性时间解决 Jeuring (1994), Gusfield (1997)提供的,基于后缀树(suffix trees)。也存在已知的高效并行算法。
该算法步骤如下:
- 在字符串首尾和字符之间插入分隔符、该分隔符不能出现在原来的字符串中
- 构造一个辅助数组p,记录扩充之后的每个字符的最大的回文半径
例如:p[3] = 4,表示以第三个位置(b)为中心,最大回文子串的半径为4(0-6)
判断是否是回文子串的代码如下: - 遍历完成之后,我们就可以根据p来获取最大回文子串了
找最大回文子串,我们直接在扩充后的列表中找,最后删除插入的字符即可。
首先,我们需要获取最大的回文子串的中心,这个可以通过p.index(max(p))来找到;
其次,我们需要找到字符开始的位置,开始的位置一定是回文子串的中心 - 回文子串的半径,即p.index(max(p)) - max(p) + 1
最后,结束的位置 = 开始的位置 + 半径 * 2
注意这里的半径包括了中心字符
代码实现:
从今日起将不提供Java代码实现
#
# @lc app=leetcode.cn id=5 lang=python
#
# [5] 最长回文子串
#
# @lc code=start
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s) <= 1:
return s
# maxStr = ''
# point = 0
s_plus = list('#' + '#'.join(s) + '#')
p = [1] * len(s_plus)
for i in range(1, len(s_plus) - 1):
while i - p[i] >= 0 and i + p[i] < len(s_plus):
if s_plus[i - p[i]] != s_plus[i + p[i]]:
break
p[i] += 1
mid = p.index(max(p))
start = mid - p[mid] + 1
end = start + (p[mid] - 1) * 2 + 1
r = ''.join(s_plus[start : end])
return r.replace("#", "")
# @lc code=end