LeetCode-14-最长公共前缀

1 问题描述

编写一个函数来查找字符串数组中的最长公共前缀
如果不存在公共前缀,返回空字符串 “”

2 示例

输入:[“flower”,“flow”,“flight”]
输出:“fl”

3 解法

3.1 解法一:水平扫描法

算法描述:
首先,将列表中的字符串按照长度重新排序,然后依次遍历字符串[S1Sn]\left[S_{1} \ldots S_{n}\right],首先求LCP(S1,S2)L C P\left(S_{1}, S_{2}\right),然后求LCP(LCP(S1,S2),S3)L C P\left(L C P\left(S_{1}, S_{2}\right), S_{3}\right),依次类推:

LCP(S1Sn)=LCP(LCP(LCP(S1,S2),S3),Sn)L C P\left(S_{1} \ldots S_{n}\right)=L C P\left(L C P\left(L C P\left(S_{1}, S_{2}\right), S_{3}\right), \ldots S_{n}\right)

图 1. 查找最长公共前缀 (水平扫描法)
Python3实现
def longestCommonPrefix(strs):
if len(strs) == 0:
return ""
else:
strs = sorted(strs, key=len)
prefix = strs[0]
for i in range(1,len(strs)):
while strs[i].find(prefix) != 0:
prefix = prefix[0:len(prefix)-1]
if len(prefix) == 0:
return ""
return prefix

3.2 解法二:分治法

算法描述
这个算法的思路来自于LCP操作的结合律。 我们可以发现:

LCP(S1Sn)=LCP(LCP(S1Sk),LCP(Sk+1Sn))L C P\left(S_{1} \ldots S_{n}\right)=L C P\left(L C P\left(S_{1} \ldots S_{k}\right), L C P\left(S_{k+1} \ldots S_{n}\right)\right)

,其中LCP(S1Sn)\operatorname{LCP}\left(S_{1} \ldots S_{n}\right)是字符串[S1Sn]\left[S_{1} \ldots S_{n}\right]的最长公共前缀,1<k<n1<k<n
为了应用上述的结论,我们使用分治的技巧,将原问题LCP(SiSj)L C P\left(S_{i} \cdots S_{j}\right)分成两个子问题LCP(SiSmid)L C P\left(S_{i} \cdots S_{m i d}\right)LCP(Smid+1,Sj)L C P\left(S_{m i d+1}, S_{j}\right)

其中mid =i+j2\frac{i+j}{2}。 我们用子问题的解 lcpLeft lcpRight 构造原问题的解。 从头到尾挨个比较 lcpLeft 与 lcpRight 中的字符,直到不能再匹配为止。 计算所得的 lcpLeft 与 lcpRight 最长公共前缀就是原问题的解。

图 2. 查找最长公共前缀的分治方法
Python3实现
def longestCommonPrefix(strs):
def recursion(strs, l, r):
if l == r:
return strs[l]
else:
mid = (l+r)//2
lcpLeft = recursion(strs, l, mid)
lcpRight = recursion(strs, mid+1, r)
return commonPrefix(lcpLeft,lcpRight)

def commonPrefix(left, right):
while right.find(left) != 0:
left = left[0:len(left)-1]
if len(left) == 0:
return ""
return left

if len(strs) == 0:
return ""
else:
return recursion(strs, 0, len(strs)-1)

3.3 解法三:二分查找法

算法描述
应用二分查找法找到所有字符串的公共前缀的最大长度 L。 算法的查找区间是(0minLen)(0 \ldots \min L e n),其中 minLen 是输入数据中最短的字符串的长度,同时也是答案的最长可能长度。 每一次将查找区间一分为二,算法进行的过程中一共会出现两种可能情况:

  • S[1…mid] 不是所有串的公共前缀。 这表明该前缀加上mid后面的字符也一定不是公共前缀,于是我们就可以丢弃后半个查找区间。

  • S[1…mid] 是所有串的公共前缀。 这表示该前缀加上mid后面的字符有可能也是公共前缀,因为我们要找最长的公共前缀,所以我们将mid后的字符附加在S[1…mid]后继续进行匹配。

图 3. 使用二分查找法寻找最长公共前缀
Python3实现
def longestCommonPrefix(strs):
def isCommonPrefix(strs, length):
for i in range(1,len(strs)):
if strs[i].find(strs[0][0:length+1]) == 0:
continue
else:
return False
return True

if len(strs) == 0:
return ""
else:
strs = sorted(strs, key=len)
low, high = 0, len(strs[0])-1
while low <= high:
mid = (low + high) // 2
if isCommonPrefix(strs, mid):
low = mid + 1
else:
high = mid - 1
print ((low + high) // 2)
return strs[0][0:(low + high) // 2 + 1]
文章作者: Alston
文章链接: https://lizitong67.github.io/2020/02/21/LeetCode-14-%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E5%89%8D%E7%BC%80/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Alston's blog