14. 最长公共前缀

解法一:行扫描法

       算法一次遍历字符串{S1, ... Sn},当遍历到第i个字符串的时候,找到最长公共前缀LCP(S1, ... , Si)。当LCP(S1, ... , Si)是一个空串的时候,算法就结束了。否则,在执行了n次遍历之后,算法就会返回最终答案LCP(S1, ... , Sn)

复杂度分析

  • 时间复杂度:O(S),S 是所有字符串中字符数量的总和。

    最坏的情况下,n 个字符串都是相同的。 算法会将 S1 与其他字符串 [S2, ... , Sn​] 都做一次比较。这样就会进行 S 次字符比较,其中 S 是输入数据中所有字符数量。

  • 空间复杂度:O(1), 我们只需要使用常数级别的额外空间。 

JAVA 

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) { // 返回指定字符在字符串中第一次出现处的索引,返回为0说明此时prefix与strs[i]有公共前缀
            prefix = prefix.substring(0, prefix.length() - 1);
            if (prefix.isEmpty()) return "";
        }
    return prefix;
}

 

解法二:列扫描法-进阶

复杂度分析

  • 时间复杂度:O(S),S 是所有字符串中字符数量的总和。

    最坏情况下,输入数据为 n 个长度为 m 的相同字符串,算法会进行 S = m*n 次比较。可以看到最坏情况下,本算法的效率与算法一相同,但是最好的情况下,算法只需要进行 n*minLen 次比较,其中 minLen 是数组中最短字符串的长度。

  • 空间复杂度:O(1), 我们只需要使用常数级别的额外空间。 

JAVA

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); // charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1
        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];
}

 

解法三:分治法

算法的思路来自于LCP操作的结合律。LCP(S1,...,Sn)=LCP( LCP(S1,...,Sk), LCP(Sk+1,...,Sn) )

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);
}

 

复杂度分析

最坏情况下,我们有 n 个长度为 m 的相同字符串。

  • 时间复杂度:O(S),S 是所有字符串中字符数量的总和,S=m*n。

    时间复杂度的递推式为 T(n)=2⋅T(2n​)+O(m), 化简后可知其就是 O(S)。最好情况下,算法会进行 minLen⋅n 次比较,其中 minLen 是数组中最短字符串的长度。

  • 空间复杂度:O(m⋅log(n))

    内存开支主要是递归过程中使用的栈空间所消耗的。 一共会进行 log(n) 次递归,每次需要 m 的空间存储返回结果,所以空间复杂度为 O(m⋅log(n))。

解法四:二分查找法

这个想法是应用二分查找法找到所有字符串的公共前缀的最大长度 L。 算法的查找区间是 (0…minLen),其中 minLen 是输入数据中最短的字符串的长度,同时也是答案的最长可能长度。 每一次将查找区间一分为二,然后丢弃一定不包含最终答案的那一个。算法进行的过程中一共会出现两种可能情况:

  • 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;
}

复杂度分析

最坏情况下,我们有 n 个长度为 m 的相同字符串。

  • 时间复杂度:O(S⋅log(n)),其中 S 所有字符串中字符数量的总和。

    算法一共会进行 log(n) 次迭代,每次一都会进行 S=m∗n 次比较,所以总时间复杂度为 O(S⋅log(n))。

  • 空间复杂度:O(1),我们只需要使用常数级别的额外空间。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值