给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
这个题用Manacher算法进行求解。
首先给的字符串s可能有偶数个或者奇数个字符,为了统一操作,在字符串的空位间插入一个字符串中不会出现的字符,比如’*‘或者’#’。
比如abc变换后成为#a#b#c#
s = '#' + '#'.join(s) + '#'
创建RL数组,存储字符串第i个位置的回文半径。比如子串长度为5的话,回文半径就是2。
maxcenter为最长的回文中心。
while i-RL[i]-1>=0 and i+RL[i]+1<len(s) and s[i-RL[i]-1] == s[i+RL[i]+1]:
RL[i] += 1
如果以当前的字符串为回文中心的回文子串其向右向左都没有超出字符串的范围,即,i-RL[i]-1>=0 and i+RL[i]+1<len(s),且扩展后仍为回文子串,即s[i-RL[i]-1] == s[i+RL[i]+1],则当前点的回文半径+1。
if i+RL[i] > maxright:
maxright = RL[i] + i
pos = i
if RL[i] > RL[maxcenter]:
maxcenter = i
if i+RL[i] > maxright,更新maxright和i。
if RL[i] > RL[maxcenter],更新maxcenter。
如果只考虑这些的话,也可以写出如下代码:
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
s = '*' + '*'.join(s) + '*'
maxcenter = 0
RL = [0] * len(s)
for i in range(len(s)):
RL[i] = 0
while i - RL[i] - 1 >= 0 and i + RL[i] + 1 < len(s) and s[i - RL[i] - 1] == s[i + RL[i] + 1]:
RL[i] += 1
if RL[i] > RL[maxcenter]:
maxcenter = i
return s[maxcenter - RL[maxcenter]:maxcenter + RL[maxcenter] + 1].replace('*','')
执行用时: 2416 ms
内存消耗: 13.1 MB
到这个地方还比较好懂,但是怎样高效地求的RL数组还需要考虑,因为前面的代码执行用时还是比较长的。
maxright代表目前所访问到的以这个字符串为回文中心的最右边界,pos为这个最右边界的回文中心的位置。maxright,pos都是为了高效地求的RL数组这个目的而设置的变量。
简单来说,就是利用前面已经求过的RL使得后面的RL少进行几次比较,就像KMP算法一样。
这个地方我参考一些博客想了好久才搞懂。
参考:
https://blog.csdn.net/xinxin100011/article/details/81235483?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161706754116780264077225%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=161706754116780264077225&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-9-81235483.first_rank_v2_pc_rank_v29&utm_term=最长回文子串+python
https://segmentfault.com/a/1190000003914228
for i in range(len(s)):
if i<maxright: #i在maxright左侧
RL[i] = min(maxright-i, RL[pos*2-i] ) #(i+j)/2 = pos
else: #i在maxright右侧(含)
RL[i] = 0
完整代码如下:
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
s = '*' + '*'.join(s) + '*'
pos = maxright = 0
maxcenter = 0
RL = [0] * len(s)
for i in range(len(s)):
if i < maxright:
RL[i] = min(maxright - i,RL[2 * pos - i])
else:
RL[i] = 0
while i - RL[i] - 1 >= 0 and i + RL[i] + 1 < len(s) and s[i - RL[i] - 1] == s[i + RL[i] + 1]:
RL[i] += 1
if i + RL[i] > maxright:
maxright = i + RL[i]
pos = i
if RL[i] > RL[maxcenter]:
maxcenter = i
return s[maxcenter - RL[maxcenter]:maxcenter + RL[maxcenter] + 1].replace('*','')
执行用时: 168 ms
内存消耗: 13 MB
这样一来,执行用时就大大减少了。