題目
編寫一個函式來查詢字串陣列中的最長公共字首。
如果不存在公共字首,返回空字串 ""。
輸入:strs = ["flower","flow","flight"]
輸出:"fl"
縱向掃描
最直接的思路:
選取第一個字串假設為字首。
用這個字首的每一個字元,去和所有其他字串比較,如果其他字串在該位置也是這個字元,那這個字元就屬於字首;反之,字首就到此結束。
不需要考慮選取陣列中的第一個字串是否長度是最小或最長的。因為最差情況就是這個字串是長度最短的,並且所有字元剛好都是字首。
時間複雜度:最壞情況,陣列有n個字串,字串全都一樣且長度為m,O(mn)
空間複雜度:O(1)
def longestCommonPrefix(strs):
if not strs:
return ""
# 第一個字串長度,字串數量
length, count = len(strs[0]), len(strs)
for i in range(length):
# 遍歷第一個字串的每個字元
c = strs[0][i]
# 從1開始遍歷字串陣列,每個字串的當前字元!=第一個字串的當前字元,
# 或者當前字元的索引==當前字串的長度(i=0時,len(strs[j])=0,說明有空字串;
# i>0時,i==len(strs[j])說明遇到了長度更小的字串,那當前字元肯定不會是公共字首了)
if any(i == len(strs[j]) or strs[j][i] != c for j in range(1, count)):
return strs[0][:i]
# 排除strs=['']或['a'],只有一個元素的情況
return strs[0]
橫向掃描
假設陣列中第一個字串是字首。
拿假設字首,和第二個字串比較,共有部分是新的字首。
再拿新的字首,和第三個字串比較,共有部分是新的字首。
一直如此,比到陣列最後一個字串。
時間複雜度:最壞情況和之前一樣,O(mn)
空間複雜度:O(1)
def longestCommonPrefix(strs):
prefix = strs[0]
for str in strs[1:]:
minLen, index = min(len(prefix), len(str)), 0
while index < minLen and prefix[index] == str[index]:
index += 1
prefix = prefix[:index]
return prefix
分治法
分治法是一種思想,就是分而治之。
遞迴是一種程式設計技巧,分治思想在程式碼中適合用遞迴來實現。
在本題中,將原陣列分成兩半,分別找出這兩部分的公共字首,再找出這兩個字首的公共字首,最終就是這個陣列的公共字首。這就是分治思想
時間複雜度O(mn)
空間複雜度O(mlogn),m是字串平均長度,n是字串數量
def longestCommonPrefix(strs):
def split(start, end):
# 分到不可再分,返回字串
if start == end:
return strs[start]
mid = (start + end) // 2
# 遞迴分割陣列
left, right = split(start, mid), split(mid + 1, end)
# 以兩個字串中較小的長度作為遍歷範圍
minLen = min(len(left), len(right))
print(left, right, minLen)
for i in range(minLen):
# 當出現不一致,則該層公共字首已找到
if left[i] != right[i]:
return left[:i]
# 沒有不一致,長度較小的字串全部遍歷完,則長度較小的字串就是公共字首
return left[:minLen]
return split(0, len(strs) - 1)
二分法
公共字首的長度一定不會超過最小長度的字串。
我們再最小長度字串上使用二分法,將其分成兩半,用左半邊字元去和每個字串比較
如果每個字串都有,那麼就移動左指標
如果有字串沒有,那麼就移動右指標,縮小公共範圍
時間複雜度:O(mnlogm),其中m是字串陣列中的字串的最小長度,n是字串的數量。二分查詢的迭代執行次數是O(logm),每次迭代最多需要比較mn個字元,因此總時間複雜度是O(mnlogm)。
空間複雜度:O(1)O(1)。使用的額外空間複雜度為常數。
def longestCommonPrefix(strs):
minLen = min(len(str) for str in strs)
left, right = 0, minLen - 1
# 迴圈條件是<=,left和right會移動到同一個字元上,有==的情況
while left <= right:
mid = (left + right) // 2
# all()函式,全部為True返回True
allTrue = all(strs[0][:mid + 1] == str[:mid + 1] for str in strs)
if allTrue:
left = mid + 1
else:
right = mid - 1
return strs[0][:left]