最长公共前缀
1.题目内容
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
1 2
| 输入:strs = ["flower","flow","flight"] 输出:"fl"
|
示例 2:
1 2 3
| 输入:strs = ["dog","racecar","car"] 输出:"" 解释:输入不存在公共前缀。
|
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成
2.解法
(1)横向扫描
思路及算法
用 LCP(S1…Sn)表示字符串 S1…Sn 的最长公共前缀。
可以得到以下结论:
LCP(S1…Sn)=LCP(LCP(LCP(S1,S2),S3),…Sn)
基于该结论,可以得到一种查找字符串数组中的最长公共前缀的简单方法。依次遍历字符串数组中的每个字符串,对于每个遍历到的字符串,更新最长公共前缀,当遍历完所有的字符串以后,即可得到字符串数组中的最长公共前缀。

如果在尚未遍历完所有的字符串时,最长公共前缀已经是空串,则最长公共前缀一定是空串,因此不需要继续遍历剩下的字符串,直接返回空串即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| //C++ class Solution { public: string longestCommonPrefix(vector<string>& strs) { if (!strs.size()) { return ""; } string prefix = strs[0]; int count = strs.size(); for (int i = 1; i < count; ++i) { prefix = longestCommonPrefix(prefix, strs[i]); if (!prefix.size()) { break; } } return prefix; }
string longestCommonPrefix(const string& str1, const string& str2) { int length = min(str1.size(), str2.size()); int index = 0; while (index < length && str1[index] == str2[index]) { ++index; } return str1.substr(0, index); } };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| //Java class Solution { public String longestCommonPrefix(String[] strs) { if (strs == null || strs.length == 0) { return ""; } String prefix = strs[0]; int count = strs.length; for (int i = 1; i < count; i++) { prefix = longestCommonPrefix(prefix, strs[i]); if (prefix.length() == 0) { break; } } return prefix; }
public String longestCommonPrefix(String str1, String str2) { int length = Math.min(str1.length(), str2.length()); int index = 0; while (index < length && str1.charAt(index) == str2.charAt(index)) { index++; } return str1.substring(0, index); } }
|
复杂度分析
(2)纵向扫描
思路及算法
纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。

代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| //Java class Solution { public String longestCommonPrefix(String[] strs) { if (strs == null || strs.length == 0) { return ""; } int length = strs[0].length(); int count = strs.length; for (int i = 0; i < length; i++) { char c = strs[0].charAt(i); for (int j = 1; j < count; j++) { if (i == strs[j].length() || strs[j].charAt(i) != c) { return strs[0].substring(0, i); } } } return strs[0]; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| //C++ class Solution { public: string longestCommonPrefix(vector<string>& strs) { if (!strs.size()) { return ""; } int length = strs[0].size(); int count = strs.size(); for (int i = 0; i < length; ++i) { char c = strs[0][i]; for (int j = 1; j < count; ++j) { if (i == strs[j].size() || strs[j][i] != c) { return strs[0].substr(0, i); } } } return strs[0]; } };
|
复杂度分析
(3)分治
思路及算法
注意到 LCP的计算满足结合律,有以下结论:
LCP(S1…Sn)=LCP(LCP(S1…Sk),LCP(Sk+1…Sn))
其中 LCP(S1…Sn)是字符串 S1…Sn 的最长公共前缀,1<k<n。
基于上述结论,可以使用分治法得到字符串数组中的最长公共前缀。对于问题 LCP(Si⋯Sj),可以分解成两个子问题 LCP(Si…Smid) 与 LCP(Smid+1…Sj),其中
mid=$\frac{i+j}{2}$。对两个子问题分别求解,然后对两个子问题的解计算最长公共前缀,即为原问题的解。

代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| //C++ class Solution { public: string longestCommonPrefix(vector<string>& strs) { if (!strs.size()) { return ""; } else { return longestCommonPrefix(strs, 0, strs.size() - 1); } }
string longestCommonPrefix(const vector<string>& strs, int start, int end) { if (start == end) { return strs[start]; } else { int mid = (start + end) / 2; string lcpLeft = longestCommonPrefix(strs, start, mid); string lcpRight = longestCommonPrefix(strs, mid + 1, end); return commonPrefix(lcpLeft, lcpRight); } }
string commonPrefix(const string& lcpLeft, const string& lcpRight) { int minLength = min(lcpLeft.size(), lcpRight.size()); for (int i = 0; i < minLength; ++i) { if (lcpLeft[i] != lcpRight[i]) { return lcpLeft.substr(0, i); } } return lcpLeft.substr(0, minLength); } };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| //Java class Solution { public String longestCommonPrefix(String[] strs) { if (strs == null || strs.length == 0) { return ""; } else { return longestCommonPrefix(strs, 0, strs.length - 1); } }
public String longestCommonPrefix(String[] strs, int start, int end) { if (start == end) { return strs[start]; } else { int mid = (end - start) / 2 + start; String lcpLeft = longestCommonPrefix(strs, start, mid); String lcpRight = longestCommonPrefix(strs, mid + 1, end); return commonPrefix(lcpLeft, lcpRight); } }
public String commonPrefix(String lcpLeft, String lcpRight) { int minLength = Math.min(lcpLeft.length(), lcpRight.length()); for (int i = 0; i < minLength; i++) { if (lcpLeft.charAt(i) != lcpRight.charAt(i)) { return lcpLeft.substring(0, i); } } return lcpLeft.substring(0, minLength); } }
|
复杂度分析
(4)二分查找
思路及算法
显然,最长公共前缀的长度不会超过字符串数组中的最短字符串的长度。用 minLength 表示字符串数组中的最短字符串的长度,则可以在 [0,minLength] 的范围内通过二分查找得到最长公共前缀的长度。每次取查找范围的中间值 mid,判断每个字符串的长度为 mid 的前缀是否相同,如果相同则最长公共前缀的长度一定大于或等于 mid,如果不相同则最长公共前缀的长度一定小于 mid,通过上述方式将查找范围缩小一半,直到得到最长公共前缀的长度。

代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| //C++ class Solution { public: string longestCommonPrefix(vector<string>& strs) { if (!strs.size()) { return ""; } int minLength = min_element(strs.begin(), strs.end(), [](const string& s, const string& t) {return s.size() < t.size();})->size(); int low = 0, high = minLength; while (low < high) { int mid = (high - low + 1) / 2 + low; if (isCommonPrefix(strs, mid)) { low = mid; } else { high = mid - 1; } } return strs[0].substr(0, low); }
bool isCommonPrefix(const vector<string>& strs, int length) { string str0 = strs[0].substr(0, length); int count = strs.size(); for (int i = 1; i < count; ++i) { string str = strs[i]; for (int j = 0; j < length; ++j) { if (str0[j] != str[j]) { return false; } } } return true; } };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| //Java class Solution { public String longestCommonPrefix(String[] strs) { if (strs == null || strs.length == 0) { return ""; } int minLength = Integer.MAX_VALUE; for (String str : strs) { minLength = Math.min(minLength, str.length()); } int low = 0, high = minLength; while (low < high) { int mid = (high - low + 1) / 2 + low; if (isCommonPrefix(strs, mid)) { low = mid; } else { high = mid - 1; } } return strs[0].substring(0, low); }
public boolean isCommonPrefix(String[] strs, int length) { String str0 = strs[0].substring(0, length); int count = strs.length; for (int i = 1; i < count; i++) { String str = strs[i]; for (int j = 0; j < length; j++) { if (str0.charAt(j) != str.charAt(j)) { return false; } } } return true; } }
|
复杂度分析