【算法】最长公共前缀

题目描述

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值