LeetCode | 14. Longest Common Prefix的三種演算法

Spicrab發表於2021-01-03

問題

Write a function to find the longest common prefix string amongst an array of strings.

Solution 1: Binary Search Using Recursion

大概的思路是:首先找到input中string的最小長度L,然後把每個string從L/2處一分為二,先比較[0, L/2]是否是common prefix。如果前一半是,則比較[L/2+1, 3/4*L];如果前一半不是,則比較[0,L/4]。

在下面這個implementation中,由於recursion完全在checkPrefix這個函式中進行,且在每一層會call這個函式兩次,因此不是很efficient。

def longestCommonPrefix(self, strs: List[str]) -> str:
	try:
        n = np.min([len(word) for word in strs])
    except ValueError:
        return ''
    if n==0:
        return ''
    strs = [word[:n] for word in strs]
    re = checkPrefix(strs, l=0, r=n)
    return strs[0][:re]

def checkPrefix(self, strs, l, r):
	# stop case
    if l == r-1:
        first = strs[0]
        for word in strs:
            if word[l]!=first[l]:
                return l
        return r
    else:
        mid = l + math.ceil((r-l)/2)
        # recursive call 1
        if checkPrefix(strs, l, mid)==mid:
        	# recursive call 2
            return checkPrefix(strs, mid, r)
        else:
            return checkPrefix(strs, l, l + math.ceil((r-l)/4))

Complexity Analysis:

  • L是input中最小的string長度,S是input string長度之和,則binary search algorithm的time complexity為O(log(L)*S)。這是因為總共會做O(log(L))次comparisons,而每次比較prefix都需要O(S)。
  • Space Complexity是O(log(L)),因為總共有O(log(L))個recursive calls,而每個的儲存空間是O(1)。

Solution 2: Iterative Binary Search

改進後的implementation不使用recursion,而是用lr兩個pointers,且在checkPrefix函式只進行prefix的比對。這樣的速度快了很多:

def longestCommonPrefix(self, strs: List[str]) -> str:
        try:
            n = np.min([len(word) for word in strs])
        except ValueError:
            return ''
        if n==0:
            return ''
        strs = [word[:n] for word in strs]
        l = 0
        r = n - 1
        while l<=r:
            mid = math.ceil((l + r)/2)
            if checkPrefix(strs, l, mid):
                l = mid + 1
            else:
                r = mid - 1
        return strs[0][:r+1]
    
    def checkPrefix(self, strs, l, r):
        first = strs[0][l:r+1]
        for word in strs:
            if first!=word[l:r+1]:
                return False
        return True

Complexity Analysis:

  • Time complexity同上,O(log(L)*S)。
  • Space Complexity O(1)。

Solution 3: Vertical Scanning

思路很簡單:把input的所有詞豎著排列起來,從最左邊開始,每次比較一個character。

(其實仔細分析一下,vertical scanning應該是最intuitive、最好implement、同時還最快的algorithm了,不知道為什麼自己的第一反應是捨近求遠去弄最不方便的recursive binary search……)

# deal with edge cases
try:
    n = np.min([len(word) for word in strs])
except ValueError:
    return ''
if n==0:
    return ''
if len(strs)==1:
    return strs[0]
strs = [word[:n] for word in strs]
# start scanning
for i in range(n):
    first = strs[0][i]
    for word in strs[1:]:
        if word[i]!=first:
            return strs[0][:i]
return strs[0]

Complexity Analysis

  • Time complexity O(S),因為worst case不過是把input裡的所有character都比對一遍罷了。
  • Space complexity O(1)。

相關文章