目錄
3.4 實現 strStr (選修字符串匹配算法:KMP) (未完)
三、字符串簡介
3.1 最長公共前綴
3.1.1 問題描述
3.1.2 求解過程
法一:注意是“公共”前綴,而 不是“出現次數最多的”前綴,因此,每次只需要判斷前綴切片出現次數是否等於列表中字符串總數即可。一旦前綴切片出現頻次小於字符串總數,則不滿足公共,停止尋找。(審題很重要!)
2020/06/04 - 24.13% - 使用了太多技巧,複雜度太高!效率很低!且冗餘計算很多!
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
num = len(strs) # 字符串總數
if num == 0: # 特殊情況 []
return ""
if num == 1: # 特殊情況 [""] ["ab"]
return strs[0]
f = "" # 初始化輸出前綴字符串
max_len = len(max(strs, key=lambda x: len(x))) # 最長字符串長度
for i in range(1, max_len+1): # 使用切片不會有 index out of range 的問題
f_temp, n_temp = collections.Counter(map(lambda y: y[:i], strs)).most_common()[0]
if n_temp == num: # 如果該前綴出現頻次=字符串總數
f = f_temp # 將其作爲輸出前綴字符串
else:
return f
return f
法二:注意到,作爲“公共”前綴,事實上根本不需要那麼多技巧和遍歷。只需要選其中一個單詞切片,遍歷數組依次對比以確定當前切片是否是“公共”的。一旦發現非公共,直接返回當前切片。否則,擴大切片範圍再遍歷一次。
2020/06/04 - 77.66% - 思路有對,可以繼續優化!
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
if len(strs) == 0:
return ""
result = "" # 初始化輸出結果
first_str = strs[0] # 首單詞
for i in range(1, len(first_str)+1): # 根據參考答案, 使用 find 可以簡化 for 後操作
cur_front = first_str[:i] # 當前前綴
#for j in range(1, length): # 用單純的 for 比 enumerate 慢 20%
# if strs[j][:i] != cur_front:
for _, cur_str in enumerate(strs):
if cur_str[:i] != front: # 但凡發現一個不一樣, 直接 return
return result
result = cur_front # 遍歷數組, 確定公共前綴
return result
法三:參考寫法,太強了,充分利用 Python 特性:zip() 拉鍊函數 + * 打包 + 集合 set 等實現最優化,化繁爲簡,佩服!經過觀察,最快的方法 無一不是使用 zip() 函數。
2020/06/04 - 99.99% - 最快!
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
res = ""
# 通過 zip 縫合所有元素, 超過最短的部分會被截斷
for i in zip(*strs):
#print(i)
if len(set(i)) == 1: # 使用集合判斷當前位置字符是否公共
res += i[0]
else:
break
return res
3.2 最長迴文子串
3.2.1 問題描述
3.2.2 求解過程
法一:樸素法,從寬到窄的滑動窗口,依次對其中的字符串切片進行正、逆序比較。一旦符合比較,就是當前最長的迴文字符串了,直接返回之。否則,找到最後也沒有,就隨意返回一個字符。複雜度 O(n^3)。
2020/06/04 - 36.83% - 看來不怎麼樣!(4752 ms太低效了)
class Solution:
def longestPalindrome(self, s: str) -> str:
length = len(s)
if length <= 1: # 特殊情況如 "" "a"
return s
times = 1 # 首次用全長窗口, 只需要比較1次
for width in range(length, 0, -1): # 滑動窗口寬度-1
for start in range(times): # 滑動窗口移動次數 times
window = s[start:start+width] # 滑動窗口內字符串切片
if window == window[::-1]: # 正序逆序比較
return window
times += 1 # 滑動窗口寬度-1時,移動次數+1
return s[0] # 如果一直找不到就隨意返回一個字符
法二:參考答案,原理待回顧。
2020/06/04 - 99.94% - 最快!(52 ms)
class Solution:
def longestPalindrome(self, s: str) -> str:
length = len(s)
if length < 2 or s == s[::-1]:
return s
else:
max_length = 1
start_pointer = 0
for i in range(1, length):
even = s[i-max_length: i+1] # 切片
odd = s[i-max_length-1: i+1] # 切片
if (i - max_length-1 >= 0) and (odd == odd[::-1]):
start_pointer = i - max_length - 1
max_length += 2
continue
if (i - max_length >= 0) and (even == even[::-1]):
start_pointer = i - max_length
max_length += 1
return s[start_pointer: start_pointer+max_length]
3.3 翻轉字符串裏的單詞
3.3.1 問題描述
3.3.2 求解過程
法一:樸素法,使用 split(" ") 將原字符串以 " " 爲間隔劃分爲字符串列表,使用 [::-1] 反轉字符串列表。注意,此時字符串列表仍包含有空字符串。爲此,通過 for 循環篩選出非空字符串並加入結果字符串 res 中,然後加入字符串間隔空格。最後,使用 str.rstrip() 刪除末端空格。複雜度 O(n)。
2020/06/06 - 90.57% - 樸素的思路
class Solution:
def reverseWords(self, s: str) -> str:
res = ""
tmp = s.split(" ")[::-1]
for word in tmp:
if word:
res += word
res += " "
return res.rstrip()
法二:組合 Python 字符串方法 split() 和 join() 實現拆分和重組,通過 list[::-1] 反轉中間形式的字符串列表。複雜度 O(n)。
2020/06/06 - 99.99% - 雖然最快,但是太依賴內建方法。
class Solution:
def reverseWords(self, s: str) -> str:
if not s:
return ""
word_list = s.split()[::-1] # 獲取所有有效的字符串列表並反轉
strs = " ".join(word_list) # 將列表元素以" "爲間隔合成新字符串
return strs
# return " ".join(s.split()[::-1]) # 以上可一言以蔽之
3.4 實現 strStr (選修字符串匹配算法:KMP)
3.4.1 問題描述
3.4.2 求解過程
法一:(基準) 直接使用 Python 字符串內置方法 str.find() 實現查找匹配。如果這樣寫,面試官肯定要把你踢出去的......
2020/06/07 - 60.60% - 看來還不是最快的!
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
return haystack.find(needle)
法二:(暴力匹配改) 不同於每次都在 haystack 上滑動窗口取出切片來與 needle 比較的暴力匹配方式,本方法先試探性比較首字母,倘若相同纔有興趣比較全部,從而節省了大量時間。複雜度 O(n) 嗎?(怎麼算啊?)
202/06/08 - 97.06% - 看來還行
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
if not needle: # len(needle) == 0 返回 0
return 0
lenh = len(haystack)
lenn = len(needle)
if lenh < lenn: # 如果長度不夠肯定不行返回 -1
return -1
for i in range(lenh-lenn+1): # 遍歷
if haystack[i] == needle[0]: # 只有首字母相同纔有興趣比較其餘部分
if haystack[i:i+lenn] == needle:
return i
return -1
法三:(KMP 算法):待續。。。