LeetCode5最長迴文子串
這兩天被這個題弄得是有些崩潰了,因為之前試的兩種方法都是超時了,所以弄得後面都有些不想弄了。還好有度娘,最後用的是從中間往兩邊擴充套件的方法得此解決,真的是如釋重負啊!廢話不說,講正文貼程式碼。
題目如下:
給定一個字串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度為1000。
示例 1:
輸入: "babad"
輸出: "bab"
注意: "aba"也是一個有效答案。
示例 2:
輸入: "cbbd"
輸出: "bb"
方法一:暴力法
有些人會忍不住提出一個快速的解決方案,不幸的是,這個解決方案有缺陷(但是可以很容易地糾正):
反轉 SS,使之變成 S'S′。找到 SS 和 S'S′ 之間最長的公共子串,這也必然是最長的迴文子串。
這似乎是可行的,讓我們看看下面的一些例子。
例如,S = {“caba”}S=“caba” , S' = {“abac”}S′=“abac”:
SS 以及 S'S′ 之間的最長公共子串為 {“aba”}“aba”,恰恰是答案。
讓我們嘗試一下這個例子:S ={“abacdfgdcaba”}S=“abacdfgdcaba” , S' = {“abacdgfdcaba”}S′=“abacdgfdcaba”:
SS 以及 S'S′ 之間的最長公共子串為{“abacd”}“abacd”,顯然,這不是迴文。
這種方法確實最好理解的額,但效率確實是低。所以放在編輯器裡面跑,都是直接報超出時間限制的錯誤的,這是最dan疼的
def longestPalindrome(self, s): s_inverse = s[::-1] max = 0 maxStr = "" if len(s) < 2: return s for start in range(len(s)): for end in range(start+1, len(s)+1): if s.count(s_inverse[start:end]) > 0: index = s.index(s_inverse[start:end]) start_inverse = len(s) - end if (end - start > max) and (index == start_inverse): max = end - start maxStr = s_inverse[start:end] return maxStr
這種方法是巧妙地運用了python 字串的一些內建函式執行的,程式碼量很少,但跑起來時間不短
大概就是報這樣的提醒,就是說你的程式沒問題,但是太low了,到後面它都不願意測試了
def longestPalindrome(self, s):
max = 0
maxStr = ""
if len(s) < 2:
return s
for start in range(len(s)):
for end in range(len(s)-1, start-1, -1):
start_copy = start
end_copy = end
while s[start_copy] == s[end_copy] and start_copy < end and end_copy >
start:
start_copy += 1
end_copy -= 1
if start_copy == end and end_copy == start:
if end - start >= max:
max = end - start
maxStr = s[start: end+1]
return maxStr
這個方法是借鑑了動態規劃的思想,但是也是效率過低,報如上的錯誤,貼出來可供參考
方法二:動態規劃
為了改進暴力法,我們首先觀察如何避免在驗證迴文時進行不必要的重複計算。考慮{“ababa”}“ababa” 這個示例。如果我們已經知道 {“bab”}“bab” 是迴文,那麼很明顯,{“ababa”}“ababa” 一定是迴文,因為它的左首字母和右尾字母是相同的。
我們給出 P(i,j)P(i,j) 的定義如下:
P(i,j) = \begin{cases} \text{true,} &\quad\text{如果子串} S_i \dots S_j \text{是迴文子串}\\ \text{false,} &\quad\text{其它情況} \end{cases}P(i,j)={true,false,如果子串Si…Sj是迴文子串其它情況
因此,
P(i, j) = ( P(i+1, j-1) \text{ and } S_i == S_j )P(i,j)=(P(i+1,j−1) and Si==Sj)
基本示例如下:
P(i, i) = trueP(i,i)=true
P(i, i+1) = ( S_i == S_{i+1} )P(i,i+1)=(Si==Si+1)
這產生了一個直觀的動態規劃解法,我們首先初始化一字母和二字母的迴文,然後找到所有三字母迴文,並依此類推…
複雜度分析
-
時間複雜度:O(n^2)O(n2), 這裡給出我們的執行時間複雜度為 O(n^2)O(n2) 。
-
空間複雜度:O(n^2)O(n2), 該方法使用 O(n^2)O(n2) 的空間來儲存表。
程式碼如下:
def longestPalindrome(self, s):
dpArray = np.zeros([1000, 1000])
maxIndex = -1
maxStr = ""
if len(s) < 2:
return s
dpArray[0][0] = 1
for end in range(1, len(s)):
dpArray[end][end] = 1
for start in range(end+1):
if end == start+1 and s[end] == s[start]:
dpArray[start][end] = 1
if end > start + 1:
dpArray[start][end] = dpArray[start+1][end-1] and (s[end] == s[start])
if end - start >= maxIndex and dpArray[start][end]:
maxIndex = end - start
maxStr = s[start:end+1]
print(maxStr)
print(dpArray[0:len(s), 0:len(s)])
return maxStr
看起來確實是牛逼了一些,但不知為何在我的電腦上跑,它還是說超出時間限制,當時真的是快把我給氣炸了
方法三:中心擴充套件演算法
事實上,只需使用恆定的空間,我們就可以在 O(n^2)O(n2) 的時間內解決這個問題。
我們觀察到迴文中心的兩側互為映象。因此,迴文可以從它的中心展開,並且只有 2n - 1個這樣的中心。
你可能會問,為什麼會是 2n - 1個,而不是 n 箇中心?原因在於所含字母數為偶數的迴文的中心可以處於兩字母之間(例如{“abba”}“abba” 的中心在兩個 {‘b’}‘b’ 之間)。
程式碼如下:
def longestPalindrome(self, s):
maxLen = 0
maxStr1 = ""
for start_current in range(len(s)):
if len(s) - start_current <= int(maxLen/2):
break
start_head = start_current
start_last = start_current + 1
while start_head >= 0 and start_last < len(s) and s[start_head] == s[start_last]:
start_head -= 1
start_last += 1
if start_last - start_head - 1 >= maxLen:
maxLen = start_last - start_head - 1
maxStr1 = s[start_head + 1: start_last]
start_head = start_current
start_last = start_current
while start_last < len(s) and start_head >= 0 and s[start_head] == s[start_last]:
start_head -= 1
start_last += 1
if start_last - start_head - 1 >= maxLen:
maxLen = start_last - start_head - 1
maxStr1 = s[start_head + 1: start_last]
return maxStr1
到了這種方法總算是給跑出來了,而且它的速度也算是中等吧!真的是一把鼻涕一把淚啊,本來想再把速度給提高的,但是被之前的失敗經歷給打擊了,容我緩一段時間再來剛,最後把它的執行時間給貼出來