題目:
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]