1 问题描述
编写一个函数来查找字符串数组中的最长公共前缀 。
如果不存在公共前缀,返回空字符串 “”
2 示例
输入:[“flower”,“flow”,“flight”]
输出:“fl”
3 解法
3.1 解法一:水平扫描法
算法描述:
首先,将列表中的字符串按照长度重新排序,然后依次遍历字符串[ S 1 … S n ] \left[S_{1} \ldots S_{n}\right] [ S 1 … S n ] ,首先求L C P ( S 1 , S 2 ) L C P\left(S_{1}, S_{2}\right) L C P ( S 1 , S 2 ) ,然后求L C P ( L C P ( S 1 , S 2 ) , S 3 ) L C P\left(L C P\left(S_{1}, S_{2}\right), S_{3}\right) L C P ( L C P ( S 1 , S 2 ) , S 3 ) ,依次类推:
L C P ( S 1 … S n ) = L C P ( L C P ( L C P ( S 1 , S 2 ) , S 3 ) , … S n ) 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) L C P ( S 1 … S n ) = L C P ( L C P ( L C P ( S 1 , S 2 ) , S 3 ) , … S n )
图 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操作的结合律。 我们可以发现:
L C P ( S 1 … S n ) = L C P ( L C P ( S 1 … S k ) , L C P ( S k + 1 … S n ) ) 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) L C P ( S 1 … S n ) = L C P ( L C P ( S 1 … S k ) , L C P ( S k + 1 … S n ) )
,其中LCP ( S 1 … S n ) \operatorname{LCP}\left(S_{1} \ldots S_{n}\right) L C P ( S 1 … S n ) 是字符串[ S 1 … S n ] \left[S_{1} \ldots S_{n}\right] [ S 1 … S n ] 的最长公共前缀,1 < k < n 1<k<n 1 < k < n 。
为了应用上述的结论,我们使用分治的技巧,将原问题L C P ( S i ⋯ S j ) L C P\left(S_{i} \cdots S_{j}\right) L C P ( S i ⋯ S j ) 分成两个子问题L C P ( S i ⋯ S m i d ) L C P\left(S_{i} \cdots S_{m i d}\right) L C P ( S i ⋯ S m i d ) 与L C P ( S m i d + 1 , S j ) L C P\left(S_{m i d+1}, S_{j}\right) L C P ( S m i d + 1 , S j ) 。
其中mid =i + j 2 \frac{i+j}{2} 2 i + j 。 我们用子问题的解 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 。 算法的查找区间是( 0 … min L e n ) (0 \ldots \min L e n) ( 0 … min L e n ) ,其中 minLen 是输入数据中最短的字符串的长度,同时也是答案的最长可能长度。 每一次将查找区间一分为二,算法进行的过程中一共会出现两种可能情况:
图 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 ]