每日leetcode——最長公共字首

Ethan發表於2022-03-02

題目

編寫一個函式來查詢字串陣列中的最長公共字首。

如果不存在公共字首,返回空字串 ""。

輸入: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]

相關文章