编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。
说明:所有输入只包含小写字母 a-z 。
解法一:逐个比较
解题思路: 从前往后一次比较字符串,获取公共前缀
var longestCommonPrefix = function(strs) {
if (strs === null || strs.length === 0) return "";
let prev = strs[0];
for(let i = 1;i<strs.length;i++){
let j = 0;
for(;j<prev.length;j++){
if(prev.charAt(j) !== strs[i].charAt(j)) break;
}
prev = prev.substring(0,j);
if(prev == "") return "";
}
return prev;
};
时间复杂度:O(s),s 是所有字符串中字符数量的总和n*m
空间复杂度:O(1)
解法二:仅需最大、最小字符串的最长公共前缀
解题思路: 获取数组中的最大值及最小值字符串,最小字符串与最大字符串的最长公共前缀也为其他字符串的公共前缀,即为字符串数组的最长公共前缀。
var longestCommonPrefix = function(strs) {
if(strs == null || strs.length == 0) return "";
let res = strs.sort();
let n = strs[strs.length - 1].length;
for(let i = 0;i<n;i++){
if(strs[0].charAt(i) !== strs[strs.length-1].charAt(i))
return strs[0].substring(0,i);
}
return strs[0];
};
时间复杂度:O(n+m),n是数组的长度, m 是字符串数组中最短字符的长度
空间复杂度:O(1)
解法三:分治策略 归并思想
分治,顾名思义,就是分而治之,将一个复杂的问题,分成两个或多个相似的子问题,在把子问题分成更小的子问题,直到更小的子问题可以简单求解,求解子问题,则原问题的解则为子问题解的合并。
这道题就是一个典型的分治策略问题:
- 问题:求多个字符串的最长公共前缀
- 分解成多个相似的子问题:求两个字符串的最长公共前缀
- 子问题可以简单求解:两个字符串的最长公共前缀求解很简单
- 原问题的解为子问题解的合并:多个字符串的最长公共前缀为两两字符串的最长公共前缀的最长公共前缀,我们可以归并比较两最长公共前缀字符串的最长公共前缀,知道最后归并比较成一个,则为字符串数组的最长公共前缀:LCP(S1,S2, …, Sn) = LCP(LCP(S1, Sk), LCP(Sk+1, Sn))
var longestCommonPrefix = function(strs) {
if (strs === null || strs.length === 0) return "";
return lCPrefixRec(strs)
};
// 若分裂后的两个数组长度不为 1,则继续分裂
// 直到分裂后的数组长度都为 1,
// 然后比较获取最长公共前缀
function lCPrefixRec(arr) {
let length = arr.length
if(length === 1) {
return arr[0]
}
let mid = Math.floor(length / 2),
left = arr.slice(0, mid),
right = arr.slice(mid, length)
return lCPrefixTwo(lCPrefixRec(left), lCPrefixRec(right))
}
// 求 str1 与 str2 的最长公共前缀
function lCPrefixTwo(str1, str2) {
let j = 0
for(; j < str1.length && j < str2.length; j++) {
if(str1.charAt(j) !== str2.charAt(j)) {
break
}
}
return str1.substring(0, j)
}
时间复杂度:O(s),s 是所有字符串中字符数量的总和
空间复杂度:O(m*logn),n是数组的长度,m为字符串数组中最长字符的长度