编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
横向扫描法(暴力):先取数组首位为目标值,再遍历其余数组元素,与目标值各个字符依次比较,遇到不等的比较下一个数组元素并更新目标值。
class Solution { public String longestCommonPrefix(String[] strs) { if(strs.length==0) return ""; StringBuilder res = new StringBuilder(strs[0]); for(int i=1; i){ int j=0; for(; j ){ if(res.charAt(j)!=strs[i].charAt(j)) break; } res.delete(j,res.length()); if(res.length()==0) return ""; } return res.substring(0); } }
/*
时间复杂度:O(mn)O(mn),其中 mm 是字符串数组中的字符串的平均长度,nn 是字符串的数量。最坏情况下,字符串数组中的每个字符串的每个字符都会被比较一次。
空间复杂度:O(1)O(1)。使用的额外空间复杂度为常数。
*/
纵向扫描法:从前往后依次遍历字符串的每一列,比较相同列的字符,直到某个字符达到长度上限或字符不等。
class Solution { public String longestCommonPrefix(String[] strs) { if(strs.length==0) return ""; for(int i=0; i){ char c=strs[0].charAt(i); for(int j=1; j ){ if(i==strs[j].length()||c!=strs[j].charAt(i)) return strs[0].substring(0,i); } } return strs[0]; } }
/*
时间复杂度:O(mn)O(mn),其中 mm 是字符串数组中的字符串的平均长度,nn 是字符串的数量。最坏情况下,字符串数组中的每个字符串的每个字符都会被比较一次。
空间复杂度:O(1)O(1)。使用的额外空间复杂度为常数。
*/
分治法:递归思想,分别找出字符串数组前一半和后一半的公共前缀,再比较这两个字符串的公共前缀。
class Solution { public String longestCommonPrefix(String[] strs) { if(strs.length==0) return ""; return recursive(strs,0,strs.length-1); } public String recursive(String[]strs,int start,int end){ if(start==end) return strs[start]; int mid=(start+end)/2; String strLeft=recursive(strs,start,mid); String strRight=recursive(strs,mid+1,end); return commonPrefix(strLeft,strRight); } public String commonPrefix(String left, String right){ for(int i=0; i){ if(left.charAt(i)!=right.charAt(i)) return left.substring(0,i); } return left.length() left:right; } }
/*
时间复杂度:O(mn)O(mn),其中 mm 是字符串数组中的字符串的平均长度,nn 是字符串的数量。时间复杂度的递推式是 T(n)=2 \cdot T(\frac{n}{2})+O(m)T(n)=2⋅T(
2
n
)+O(m),通过计算可得 T(n)=O(mn)T(n)=O(mn)。
空间复杂度:O(m \log n)O(mlogn),其中 mm 是字符串数组中的字符串的平均长度,nn 是字符串的数量。空间复杂度主要取决于递归调用的层数,层数最大为 \log nlogn,每层需要 mm 的空间存储返回结果。
*/
二分法:依旧是用目标值遍历数组的思想。不过重点在于快速找到前缀的末索引,先将索引缩小到最小的数组元素长度,再对该索引用二分查找,如果中间值是公共前缀,则目标值大于等于它,否则相反。
该方法优化横向扫描法。
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; } }
/*
时间复杂度:O(mn \log m)O(mnlogm),其中 mm 是字符串数组中的字符串的最小长度,nn 是字符串的数量。二分查找的迭代执行次数是 O(\log m)O(logm),每次迭代最多需要比较 mnmn 个字符,因此总时间复杂度是 O(mn \log m)O(mnlogm)。
空间复杂度:O(1)O(1)。使用的额外空间复杂度为常数。
*/
排序后比较首尾元素 摘自该用户在题解下评论
//计算复杂度应考虑排序字符串数组
class Solution { public: string longestCommonPrefix(vector<string>& strs) { if(strs.empty()) return string(); sort(strs.begin(), strs.end()); string st = strs.front(), en = strs.back(); int i, num = min(st.size(), en.size()); for(i = 0; i < num && st[i] == en[i]; i ++); return string(st, 0, i); } };