【LeetCode】14.最长公共前缀(python版)

题目描述
编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 1:

输入: ["flower","flow","flight"]
输出: "fl"

示例 2:

输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

说明:

所有输入只包含小写字母 a-z 。

思路

本题虽然只是“容易”等级的题目,但是可以有至少五种解法,而且这是一个在搜索领域中很常见的问题,所以还是值得一看。

  1. 暴力解法
    先找到两个字符串的公共前缀,再将前缀与第三个字符串比较…,这种方法最坏情况下的时间复杂度为 O ( S ) O(S) O(S),其中 S 代表所有字符串的总长度。
    【LeetCode】14.最长公共前缀(python版)_第1张图片
class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        if len(strs) == 0:
            return ''
        s = strs[0]
        for t in strs[1:]:
            long_common_prefix = ''
            for p, q in zip(s, t):
                if p == q:
                    long_common_prefix += p
                else:
                    break
            s = long_common_prefix
            # 出现空串时提前结束
            if s == '':
            	return ''
        return s
  1. 暴力算法的实现上可以稍微转变思路,传统的寻找共同子串是一个“加法”操作,每找到一个相同字符就就添加进变量 l o n g _ c o m m o n _ p r e f i x long\_common\_prefix long_common_prefix 。实际上也可以采用“减法”操作,在 t 串中寻找 s 串,假如没找到或者不是在开头,就缩短 s 串。这样实际上并没有减少时间复杂度,只是写法上更简单。
class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
	    if len(strs) == 0:
	            return ''
        long_common_prefix = strs[0]
        for s in strs[1:]:
            while s.find(long_common_prefix) != 0:
                long_common_prefix = long_common_prefix[:-1]
                if long_common_prefix == '':
                    return ''
        return long_common_prefix
  1. 纵向扫描:可以想象把所有字符串堆叠起来,依次比较所有字符串的第一个字符、第二个字符、第三个字符…,直到遇到某个位置的字符不全相等。同样需要比较所有字符,所以时间复杂度不变
    【LeetCode】14.最长公共前缀(python版)_第2张图片
class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        if len(strs) == 0:
            return ''
        for i in range(len(strs[0])):
            for s in strs[1:]:
                if i == len(s) or s[i] != strs[0][i]:
                    return s[:i]
        return strs[0]
  1. 分治法:对于方法2,可以采用分治思想,将数组从中间一分为二,分别求两边子数组的最长公共前缀。而每一个子数组也可以一分为二…

    分治法实际上也没有减少比较的次数,因此时间复杂度不变。同时因为采用了递归,所以空间复杂度为 O ( m ⋅ l o g ( n ) ) O(m\cdot log(n)) O(mlog(n)), n为字符串个数,m为字符串长度
    【LeetCode】14.最长公共前缀(python版)_第3张图片

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        if len(strs) == 0:
            return ''
        return self.split_list(strs, 0, len(strs)-1)

    def split_list(self, strs, start, end):
        if start == end:
            return strs[start]
        else:
            middle = (end + start) // 2
            left_str = self.split_list(strs, start, middle)
            right_str = self.split_list(strs, middle + 1, end)
            return self.get_common_prefix(left_str, right_str)

    def get_common_prefix(self, s, t):
        min_len = min(len(s), len(t))
        for i in range(min_len):
            if s[i] != t[i]:
                return s[:i]
        return s[:min_len]
  1. 二分查找法
    这里借用官方题解的图进行说明。首先找到所有字符串最短的长度,因为这是最终答案的最大可能长度。根据这个最短字符串S进行二分查找,那么查找区间为 ( 0 , m i n L e n ) (0, minLen) (0,minLen)。每次区间一分为二,丢弃不可能包含正确答案的那一半。具体来说二分查找中会出现两种情况:
    ① S[0, mid]是其余所有字符串的前缀,那么对于S[0, i] (i < mid)来说,它都是所有串的公共前缀。由于想要找的是最长的公共前缀,所以可以把前半个区间丢弃
    ② S[0, mid]不是其余所有字符串的前缀,那么对于S[0, i] (i > mid)来说,它更不可能是所有串的公共前缀,所以可以丢弃后半个区间
    【LeetCode】14.最长公共前缀(python版)_第4张图片
class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        if len(strs) == 0:
            return ''
            
        def is_common_prefix(prefix, str_list):
            for s in str_list:
                if s[:len(prefix)] != prefix:
                    return False
            return True
            
        min_str = min(strs, key=len)
        start, end = 0, len(min_str)
        while start < end:
            mid = (end + start + 1) // 2
            if is_common_prefix(min_str[:mid], strs):
                start = mid
            else:
                end = mid - 1
        return min_str[:(end + start) // 2]
  1. 一些使用 Python 的奇淫技巧(来自于官方讨论区中 @xshura 用户的回答):
    ① Python 中字符串是可以进行比较的,原生的 min()和 max()函数甚至 sort()函数会根据单个字符的 ascii码排序,例abb, aba,abac,最大为abb,最小为aba。所以只需要比较最大最小的公共前缀就是整个数组的公共前缀
    def longestCommonPrefix(self, strs):
        if not strs: return ""
        s1 = min(strs)
        s2 = max(strs)
        for i,x in enumerate(s1):
            if x != s2[i]:
                return s2[:i]
        return s1

②. 方法三如果使用python,可以有更加简单的写法

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        s = ""
        """ 
        >>> a = [[1,2,3], [4,5,6]]
        >>> zip(*a)
        >>> [[1,4], [2,5], [3,6]]
        """
        for i in zip(*strs):
            if len(set(i)) == 1:
                s += i[0]
            else:
                break
        return s

你可能感兴趣的:(LeetCode,动态规划)