题目描述
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:
输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
解题
使用分治法求解,找出左侧部分的最大公共前缀与右侧部分的最大公共前缀进行比较。
public String longestCommonPrefix(String[] strs) {
if(strs.length == 0) return "";
if(strs.length == 1) return strs[0];
int lt = 0;
int rt = strs.length-1;
int mid = lt+(rt-lt)/2;
String[] ls = new String[mid+1];
String[] rs = new String[rt-mid];
System.arraycopy(strs, 0, ls, 0, mid+1);
System.arraycopy(strs, mid+1, rs, 0, rt-mid);
String lprefix = longestCommonPrefix(ls);
String rprefix = longestCommonPrefix(rs);
int index=0;
boolean f = true;
for(int i=0; i<lprefix.length() && i<rprefix.length(); i++){
if(lprefix.charAt(i)!=rprefix.charAt(i)){
index = i;
f = false;
break;
}
}
if(f){
return lprefix.length()<rprefix.length() ? lprefix:rprefix;
}
return lprefix.substring(0, index);
}
来自官方的题解
方法一:水平扫描法
先以第一个字符串为前缀,与后面的比较找出新的公共前缀,以此类推遍历数组。indexOf(prefix)若找不到公共串则返回-1,然后将前缀从末尾去掉一个字符继续比较。
public String longestCommonPrefix(String[] strs) {
if (strs.length == 0) return "";
String prefix = strs[0];
for (int i = 1; i < strs.length; i++)
while (strs[i].indexOf(prefix) != 0) {
prefix = prefix.substring(0, prefix.length() - 1);
if (prefix.isEmpty()) return "";
}
return prefix;
}
方法二:水平扫描
(名字是不是错了?)
依次取第一个字符串的每个字符与其他字符串相应的字符进行比较,若不等或当前字符索引等于数组中最短的字符串长度(说明这个最短字符串就是最长前缀),则找出结果。
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) return "";
for (int i = 0; i < strs[0].length() ; i++){
char c = strs[0].charAt(i);
for (int j = 1; j < strs.length; j ++) {
if (i == strs[j].length() || strs[j].charAt(i) != c)
return strs[0].substring(0, i);
}
}
return strs[0];
}
方法三:分治法
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) return "";
return longestCommonPrefix(strs, 0 , strs.length - 1);
}
private String longestCommonPrefix(String[] strs, int l, int r) {
if (l == r) {
return strs[l];
}
else {
int mid = (l + r)/2;
String lcpLeft = longestCommonPrefix(strs, l , mid);
String lcpRight = longestCommonPrefix(strs, mid + 1,r);
return commonPrefix(lcpLeft, lcpRight);
}
}
String commonPrefix(String left,String right) {
int min = Math.min(left.length(), right.length());
for (int i = 0; i < min; i++) {
if ( left.charAt(i) != right.charAt(i) )
return left.substring(0, i);
}
return left.substring(0, min);
}
方法四:二分法
这个二分法分的是字符串,上面的分治法分的是数组。
每一次将查找区间一分为二,然后丢弃一定不包含最终答案的那一个。算法进行的过程中一共会出现两种可能情况:
S[1…mid] 不是所有串的公共前缀。 这表明对于所有的 j > i S[1…j] 也不是公共前缀,于是我们就可以丢弃后半个查找区间。
S[1…mid] 是所有串的公共前缀。 这表示对于所有的 i < j S[1…i] 都是可行的公共前缀,因为我们要找最长的公共前缀,所以我们可以把前半个查找区间丢弃。
简单来说就是:
将一个字符串二分,取前半段进行比较,若这前半段是所有串前缀,就说明公共前缀是前半段加上后半段的一部分,那么前半段可以舍去,再去后半段二分查找。
若前半段不是所有串前缀,则说明公共前缀在前半段,后半段就不用管了。
这里二分字符串要按照最短字符串的长度取中值。
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0)
return "";
int minLen = Integer.MAX_VALUE;
//找出最短长度
for (String str : strs)
minLen = Math.min(minLen, str.length());
int low = 1;
int high = minLen;
while (low <= high) {
int middle = (low + high) / 2;
if (isCommonPrefix(strs, middle))
low = middle + 1;
else
high = middle - 1;
}
return strs[0].substring(0, (low + high) / 2);
}
private boolean isCommonPrefix(String[] strs, int len){
String str1 = strs[0].substring(0,len);
for (int i = 1; i < strs.length; i++)
if (!strs[i].startsWith(str1))
return false;
return true;
}