【探索-字节跳动】最长公共前缀

在这里插入图片描述

解法一

先找出最短的那个字符串,然后遍历该字符串的每一个字符,并与剩余字符串的对应位置的字符比较,如果有出现不一致的时候,就表明公共前缀截止了。

// 执行用时: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值