今天做的是LeetCode习题10,寻找最长的前缀。用的是最一般的方法,很好理解,但是方法比较笨。
具体思路就是先获取字符串数组中每一个字符串的长度,找到最小值。用一个嵌套循环,找到相同的前缀,完成任务。
代码如下:
public String longestCommonPrefix(String[] strs) {
if(strs.length==0)
return null;
String result="";
int lengthOfStrs=java.lang.Integer.MAX_VALUE;
for(int i=0;i<strs.length;i++){
if(strs[i].length()<lengthOfStrs)
lengthOfStrs=strs[i].length();
}
for(int j=0;j<lengthOfStrs;j++){
for(int i=0;i<strs.length-1;i++){
if(strs[i].charAt(j)!=strs[i+1].charAt(j))
return result;
}
result+=strs[0].charAt(j);
}
return result;
}
重点是for循环。 注意最外层的循环是按列增加的。比较的是当前字符串的第j个位置和后一个字符串的第j个位置。如果一旦发现不同则直接返回之前的字符串,否则讲这个字符加到结果中。
参看官网上有4种很棒的方法:
1.Horizontal Scanning
采用的思想就是先跳出第一个和第二个的共同前缀,再挑出此前缀和第三个的共同前缀,以此类推。
官方给的代码也很值得学习。
public String longestCommonPrefix(String[] strs) {
if (strs.length == 0) return "";
String prefix = strs[0];int a=0;
for (int i = 1; i < strs.length; i++)
while ((a=strs[i].indexOf(prefix) )!= 0) {
prefix = prefix.substring(0, prefix.length() - 1);
if (prefix.isEmpty()) return "";
}
return prefix;
}
2.Vertical Scanning
第二种方法和我采取的思路是一样的,但是代码要比我写的好的多。
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];
}
首先,并不提前计算最短的字符串长度。一开始我计算的原因是防止发生数组越界的问题,但这个方法直接使用了第一个字符串的长度,在判断条件中加了是否当前指数等于字符串长度的判断,避免了越界问题,加快了运行速度。
直接以第一个字符串为依托,与其余的字符串进行比较,如果第j个字符串的第i个位置的字符不相同或者是超过长度时则直接返回第一个字符串之前的部分。不用再用另外一个变量存储结果了。
所谓vertical指的就是依次比较所以字符串中的第j列。
substring(int begin,int length):测试过,第二个变量是长度,所以在调用substring时返回的正好是第0到第i-1位字符。
3.Divide and Conquer
第三种方法采用的是分治策略。先将全部字符串不断向下一份为二,直到每组中只有一个或者两个的时候算出这两个的公共前缀,之后再依次合并两组之间的公共前缀,解决问题。
这种方法在较好情况下可以降低时间复杂度。
代码如下:
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);
}
看着比较多,其实逻辑还是很简单的。尤其在找出相同前缀的时候,因为是找两个结果前缀的,所以直接依次向下比较第i个位置的字符就可以了。
4.Binary Search
这种方法较第二种方法进行了改进,第二种方法是直接从第一个位置开始向后搜索的,这种方法是从中间位置开始,如果到中间位置存在公共前缀是成立的,则搜索长度++,否则--,直到low<high。
代码如下:
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;
}
这个方法把时间复杂度降低到了log级别。