题目:
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
输入: "cbbd" 输出: "bb"
按照惯例,先都使用暴力方法,找出所有回文子串比较长度。不过,提交超时,哈哈哈哈。
class Solution(object):
def isPalindrome(self, substring):
length = len(substring)
for i in range(length // 2):
if substring[i] != substring[length - i - 1]:
return False
return True
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if s == "":
return s
longestPalindrome = s[0]
for i in range(2, len(s) + 1):
for j in range(len(s) + 1 - i):
substring = s[j : j + i]
if self.isPalindrome(substring):
longestPalindrome = substring
break
return longestPalindrome
复杂度分析:
时间复杂度:,第一层循环是回文长度,从2到n,第二层循环遍历字符串,虽然减少了部分遍历,但两层循环也达到级别,判断是否回文又是一个n级别,总体达级别。
空间复杂度:
暴力方法改进,奇数长度的回文子串必然包含更小奇数长度的回文子串,偶数长度的回文子串也必然包含更小偶数长度的回文子串,比如长度为3的回文子串中包含长度为1的回文子串,长度为5的回文子串中包含长度为3的回文子串,若长度为3的回文子串找不到,则不需要再找,偶数长度同理。因此,可提前结束查找,大大减少计算量。改进后提交成功,执行用时500ms左右。
class Solution(object):
def isPalindrome(self, substring):
length = len(substring)
for i in range(length // 2):
if substring[i] != substring[length - i - 1]:
return False
return True
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if s == "":
return s
longestPalindrome = s[0]
even_found = True
odd_found = True
for i in range(2, len(s) + 1):
if i % 2 == 0 and even_found:
for j in range(len(s) + 1 - i):
substring = s[j : j + i]
if self.isPalindrome(substring):
even_found = True
longestPalindrome = substring
break
else:
even_found = False
if i % 2 != 0 and odd_found:
for j in range(len(s) + 1 - i):
substring = s[j : j + i]
if self.isPalindrome(substring):
odd_found = True
longestPalindrome = substring
break
else:
odd_found = False
return longestPalindrome
复杂度分析:
时间复杂度:,虽然达到提前结束查找的效果,但最坏情况下,依旧是级别。
空间复杂度:
再改进,去除判断回文函数,使用字符串反转判断,可达200ms,增加初始判断条件,可达100ms。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s) < 2 or s == s[::-1]:
return s
longestPalindrome = s[0]
even_found = True
odd_found = True
for i in range(2, len(s) + 1):
if i % 2 == 0 and even_found:
for j in range(len(s) + 1 - i):
substring = s[j : j + i]
if substring == substring[::-1]:
even_found = True
longestPalindrome = substring
break
else:
even_found = False
if i % 2 != 0 and odd_found:
for j in range(len(s) + 1 - i):
substring = s[j : j + i]
if substring == substring[::-1]:
odd_found = True
longestPalindrome = substring
break
else:
odd_found = False
return longestPalindrome
复杂度分析:
时间复杂度:,步长逆序操作减少了回文判断的复杂度。
空间复杂度:
再改进,通过判断切片起止字符是否相同,再进行回文判断,减少回文判断次数。达到64ms。复杂度并无改进。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s) < 2 or s == s[::-1]:
return s
longestPalindrome = s[0]
even_found = True
odd_found = True
for i in range(2, len(s)):
if i % 2 == 0 and even_found:
for j in range(len(s) + 1 - i):
if s[j] == s[j + i -1]:
substring = s[j : j + i]
if substring == substring[::-1]:
even_found = True
longestPalindrome = substring
break
else:
even_found = False
if i % 2 != 0 and odd_found:
for j in range(len(s) + 1 - i):
if s[j] == s[j + i -1]:
substring = s[j : j + i]
if substring == substring[::-1]:
odd_found = True
longestPalindrome = substring
break
else:
odd_found = False
return longestPalindrome
再改进,原先以回文长度递增查找,但若先查奇数,则偶数可从最大的奇数长度+1开始,执行时间52ms。复杂度依旧无改进,但感觉缩减查找次数已经做到了极致。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s) < 2 or s == s[::-1]:
return s
longestPalindrome = s[0]
even_found = True
odd_found = True
max_even = 2
for i in range(2, len(s), 2):
if even_found:
for j in range(len(s) + 1 - i):
if s[j] == s[j + i -1]:
substring = s[j : j + i]
if substring == substring[::-1]:
even_found = True
longestPalindrome = substring
max_even = i
break
else:
even_found = False
for i in range(max_even + 1, len(s), 2):
if odd_found:
for j in range(len(s) + 1 - i):
if s[j] == s[j + i -1]:
substring = s[j : j + i]
if substring == substring[::-1]:
odd_found = True
longestPalindrome = substring
break
else:
odd_found = False
return longestPalindrome
最后是在python提交执行时间靠前的算法上改进,达到了40ms,超越了百分百的提交。该种算法采用中心移动扩展,也是一种滑动窗口的方法。我对这种算法进行了分奇偶的改进,达到了目前最快执行时间。
先考虑偶数长度回文,想象字符串中每两个字符间的空隙为中心,则有n-1个中心,假设在某个中心查找2长度回文,若找到,则应向两边各扩展1查找4长度回文,但此时中心会自动增加1,则右边的扩展不需要管,而左边需要在原先的扩展基础上多扩展2,若找不到4长度回文,则什么也不用做,中心继续增大,继续查找4长度回文。
在找到的最长的偶数回文基础上,将偶数长度+1开始查找奇数长度,方法同上。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s) < 2 or s == s[::-1]:
return s
length = 1
even_medium = 0
even_left = 0
for i in range(len(s) - 1):
if i - even_left >= 0 and s[i - even_left] == s[i + 1]:
substring = s[i - even_left: i + 2]
if substring == substring[: : -1]:
even_medium = i
length = even_left + 2
even_left = even_left + 2
odd_medium = 0
half = (length + 1) // 2
odd_left = (length + 1) // 2
for j in range(half, len(s) - half):
if j - odd_left >= 0 and s[j - odd_left] == s[j + half]:
substring = s[j - odd_left: j + half + 1]
if substring == substring[: : -1]:
odd_medium = j
length = odd_left + half + 1
odd_left = odd_left + 2
if length % 2 == 0:
return s[even_medium + 2 - length: even_medium + 2]
else:
if length == 1:
return s[0]
else:
return s[odd_medium + half + 1 - length: odd_medium + half + 1]
复杂度分析:
时间复杂度:,最多有2n-1个中心
空间复杂度: