题目:
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of sis 1000.
举例:
Input: "babad" Output: "bab" Note: "aba" is also a valid answer.
Input: "cbbd" Output: "bb"
本题是最长回文串问题。其几种解法非常经典,之前对于马拉车算法不太熟悉,在这里也一并回顾一下。
一、暴力解法
遍历所有字符串,分别判断其是否为回文串。在遍历过程中,记录最长的字符串。
1. 如下字符串"abcdede", 首先选取字符串,共有 种,若长度为 n,为
, 时间复杂度O(n^2).
| a | b | c | d | e | d | e |
2. 第二步,分别判断是否为回文串,头尾各一指针,判断是否相等。
因而 总的时间复杂度为 O(n^3),时间复杂度太高,不能通过。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
n = len(s)
maxval = ""
if len(s)<=1:
return s
def isPalindrome(s):
n = len(s)
i = 0
j = n-1
while(i<=j):
if s[i]==s[j]:
i += 1
j -= 1
else:
break
if i<j:
return False
else:
return True
i = 0
while i<n:
j = i+1
while j <n+1:
if isPalindrome(s[i:j]):
if len(maxval)<j-i:
maxval = s[i:j]
j += 1
i += 1
return maxval
二、 中间指针法
不从两头判断,从中间往外扩散。整体时间复杂度为O(n^2)。
1. 第一层循环,遍历所有元素;
2. 第二层循环,往外扩散,记录最长回文串。
这里值得注意的是,需要对奇偶情况做一个区分:例如 “aba" 和 ”abba"均为回文串,但是遍历方法有区别。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
n = len(s)
maxlength = 0
val = (0,0)
def count_length(s,i,j):
while i>= 0 and j<n and s[i] == s[j]:
i -= 1
j += 1
return j-i-1,(i,j)
for i in range(n):
single,pair_s = count_length(s,i,i)
double,pair_d = count_length(s,i-1,i)
if single > double and single >maxlength:
maxlength = single
val = pair_s
if double > single and double > maxlength:
maxlength = double
val = pair_d
return s[val[0]+1:val[1]]
三、马拉车算法
马拉车算法是基于二的一个改进,将时间复杂度进一步提高到O(n)。
主要是两个方面,首先简化奇偶判断,通过插入“#”,保证了长度为奇数(2n+1)。
举例: bob --> #b#o#b#
noon --> #n#o#o#n#
其次,主要在减少遍历上做了工作,增加了一个数组,记录以其为中心时的最大字符串长度,
例如: # a # b # c # b # d
[ 1 2 1 2 1 4 1 2 1 1 ]
如果按照常规,需要对每个元素进行遍历判断最长回文串,但根据该算法的规则可以减少一些判断,从而降低时间复杂度:
可见下图,以 id 为中心的最长回文串范围为 C = [mx的对称点,mx],如果点 i 在以 id 为中心的最长回文串范围内,其关于id 的对称点为 j(j=2*id-i),根据回文串的对称特性,i 在范围 C 内与 j 对称,以 i 为中心的回文串的长度和 j 相同,为 Lj 。因而如果 j 的回文串范围在 mx 以内,i 的长度直接为 j 的长度 Lj。如果超过该范围,则超过部分需要继续判断。这一部分是核心,剩余就是如果 i > mx,需要按之前方式判断。
总结:
if mx > i, 则 p[i] = min( p[2 * id - i] , mx - i ),这里又分为两个部分,一个是在范围内,一个是超过范围,分开讨论;
else,p[i] = 1,继续判断
执行时间108ms,相比而言,速度较快。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
new_s = list('#' + '#'.join(s) + '#')
mx = 0
val = (0,0)
ids = 0
matrix = [0 for _ in range(len(new_s))]
def count_length(s,i,j):
cnt = 0
while i>=0 and j<len(s) and s[i] == s[j]:
i -= 1
j += 1
cnt += 1
return cnt,(i+1,j)
for i in range(len(new_s)):
if i < mx:
j = 2 * ids - i
if matrix[j] < mx - i:
matrix[i] = matrix[j]
else:
res,pair = count_length(new_s,2*i-mx,mx)
matrix[i] = mx -i + res
ids = i
if (val[1] - val[0]+1)/2 < matrix[i]:
val = pair
mx = i + matrix[i] - 1
else:
l,pair = count_length(new_s,i,i)
mx = i + l - 1
ids = i
matrix[i] = l
if (val[1] - val[0]+1)/2 < l:
val = pair
return s[val[0]/2:val[1]/2]