解法一
先找出最短的那个字符串,然后遍历该字符串的每一个字符,并与剩余字符串的对应位置的字符比较,如果有出现不一致的时候,就表明公共前缀截止了。
// 执行用时:11 ms
public String longestCommonPrefix(String[] strs) {
if (strs==null||strs.length==0) return "";
if (strs.length==1) return strs[0];
int minLen = strs[0].length();
for (int i = 1; i < strs.length; i++) {
int l = strs[i].length();
if (l<minLen) minLen = l;
}
for (int i = 0; i < minLen; i++) {
char curChar = strs[0].charAt(i);
for (int j = 1; j < strs.length; j++) {
if (curChar!=strs[j].charAt(i)) {
return strs[0].substring(0, i);
}
}
}
return strs[0].substring(0, minLen);
}
也可以先按照字符串长度先对所有字符串排序。
// 执行用时:8 ms
public longestCommonPrefix(String[] strs) {
if (strs==null||strs.length==0) return "";
if (strs.length==1) return strs[0];
Arrays.sort(strs);
for (int i = 0,l = strs[0].length(); i < l; i++) {
char curChar = strs[0].charAt(i);
for (int j = 1; j < strs.length; j++) {
if (curChar!=strs[j].charAt(i)) {
return strs[0].substring(0, i);
}
}
}
return strs[0];
}
在提交记录中显示利用排序的比较快。
解法二
不同事先找出最短的那一个字符串元素,可以根据木桶原则,最长的公共前缀肯定与其中最短的那个元素有关,因为可以在遍历每个元素的时候再去判断。
// 执行用时:10 ms
public String longestCommonPrefix(String[] strs){
if (strs == null || strs.length == 0) return "";
int minLen = 0;
while (true) {
// 如果 minLen 达到了 strs[0] 对应的长度,则表示最长前缀即为 strs[0]
if (minLen == strs[0].length()) return strs[0];
char c = strs[0].charAt(minLen);
for (int i=1; i < strs.length; i++) {
String tmp = strs[i];
// 这里的判断遵循木桶原则,即最长相同前缀与最短的那个 strs[0] 有关
if (minLen==tmp.length()|| c!= strs[i].charAt(minLen)) {
return strs[0].substring(0, minLen);
}
}
++minLen;
}
}
解法三
前面的是从头向尾去遍历比较,当然也可以从最短的字符串的尾部开始比较。
来自执行用时为 6 ms 的其他范例
public static String longestCommonPrefix2(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
int Length_min = strs[0].length();
String temp = "";
for (String m : strs) {
int length1 = m.length();
if (length1 <= Length_min) {
Length_min = length1;
temp = m;
}
}
int length = temp.length();
// 反着来,先从能够出现的最长的前缀来判断,如果不行,就减去末尾匹配的字符
for (int i = length; i > 0; i--) {
String temp1 = temp.substring(0, i);
boolean flag = true;
for (String m : strs) {
String temp2 = m.substring(0, i);
if (!temp2.equals(temp1)) {
flag = false;
break;
}
}
// 因为 temp1 是尾部向有缩减的
// 如果 flag 为 true,就表示 temp1 是最长的符合要求的答案
if (flag) return temp1;
}
return "";
}
还是基于从尾到头的思想,只不过借助 String#startsWith()
方法来判断目标字符串是不是以某字符串作为前缀。
// 执行用时:9 ms
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
int Length_min = strs[0].length();
String temp = "";
for (String m : strs) {
int length1 = m.length();
if (length1 <= Length_min) {
Length_min = length1;
temp = m;
}
}
int length = temp.length();
// 反着来,先能够出现的最长的前缀来判断,如果不行,就减去末尾匹配的字符
boolean flag = true;
for (int i = length; i > 0; i--) {
String temp1 = temp.substring(0, i);
for (String m : strs) {
if (!m.startsWith(temp1, 0)) {
flag = false;
break;
}
}
if (flag) return temp1;
flag = true;
}
return "";
}
来自执行用时为 5 ms 的其他范例
还是借助 String#startsWith()
方法,但是更加简洁,省去了寻找元素中最短的字符串的步骤。
当然,这里还有一个特点,就是每次都与前一个字符串比较,得到当前两个字符串的最长公共前缀,然后再拿该前缀与一个字符串比较,又得到两者的最长公共前缀,如此往复
而不是像之前那样,先得到一个最短字符串,然后依次与每个字符串去比较,如果不是这些字符串的最长公共前缀,则去掉末尾的字符,形成新的字符串再去依次与每个字符串比较,如此往复。
在效率上,先得到前后两个相邻字符串的最长公共前缀,然后再与新的字符串比较,这样效率会高点,因为遵循木桶效应,只要任意两个字符串的最长公共前缀 common1
长度小于其他剩余的任意两个字符串的最长公共前缀 commonX
,则整个所有字符串元素的最长公共前缀肯定就要缩小到 common1
的范围内了。
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
if (strs.length == 1) {
return strs[0];
}
String result = strs[0];
for (int i = 1; i < strs.length; i++) {
for (int j = result.length(); j >= 0; j--) {
if (strs[i].startsWith(result.substring(0, j))) {
break;
} else {
result = result.substring(0, j - 1);
}
if ("".equals(result)) {
return "";
}
}
}
return result;
}