「力扣」14. 最长公共前缀

「力扣」14. 最长公共前缀

题目描述

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

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

示例 1:

输入: strs = ["flower","flow","flight"]
输出: "fl"

示例 2:

输入: strs = ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

提示:

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] 仅由小写英文字母组成

题解

解法1(横向搜索)

可以先求两个字符串的最大公共前缀,然后在将计算结果与后面的字符串再求最大公共前缀,以此类推,如下图所示:

最大公共前缀.png
字符串数组是:[“flower”,“flow”,“flight”],先求flowerflow的最大公共前缀,结果是flow(图中红色部分),然后再用这个计算结果flow与字符串数组后面的flight计算最大公共前缀,得到结果fl(图中绿色部分)。

代码如下:

class Solution {
    public String longestCommonPrefix(String[] strs) {
        String lcp = strs[0];
        for (int i = 1; i < strs.length; i++) {
            lcp = longestCommonPrefix(lcp, strs[i]);
        }
        return lcp;
    }

    private String longestCommonPrefix(String s1, String s2) {
        char[] s1Array = s1.toCharArray();
        char[] s2Array = s2.toCharArray();
        String ans = "";
        for (int i = 0; i < Integer.min(s1Array.length, s2Array.length); i++) {
            if (s1Array[i] == s2Array[i]) {
                ans += s1Array[i];
            } else {
                break;
            }
        }
        return ans;
    }
}

提交结果如下:

image.png

执行时间只击败了10.26%的java用户,内存43.76M,只击败了5.02%的java用户,写的这么烂吗?烂在哪里了?内存使用过高?
char[] s1Array = s1.toCharArray(); char[] s2Array = s2.toCharArray(); 这里不转化为数组可能会好一点, 修改代码如下:

class Solution {
    public String longestCommonPrefix(String[] strs) {
        String lcp = strs[0];
        for (int i = 1; i < strs.length; i++) {
            lcp = longestCommonPrefix(lcp, strs[i]);
        }
        return lcp;
    }

    private String longestCommonPrefix(String s1, String s2) {
        String ans = "";
        for (int i = 0; i < Integer.min(s1.length(), s2.length()); i++) {
            char c1 = s1.charAt(i);
            char c2 = s2.charAt(i);
            if (c1 == c2) {
                ans += c1;
            } else {
                break;
            }
        }
        return ans;
    }
}

提交:

image.png

额,没啥效果。

难道是多次字符串拼接导致的运行时间长?可以只计算索引,最后截取字符串。

class Solution {
    public String longestCommonPrefix(String[] strs) {
        String lcp = strs[0];
        for (int i = 1; i < strs.length; i++) {
            lcp = longestCommonPrefix(lcp, strs[i]);
        }
        return lcp;
    }
    private String longestCommonPrefix(String s1, String s2) {
        int index = 0;
        for (int i = 0; i < Integer.min(s1.length(), s2.length()); i++) {
            char c1 = s1.charAt(i);
            char c2 = s2.charAt(i);
            if (c1 == c2) {
                index++;
            } else {
                break;
            }
        }
        return s1.substring(0, index);
    }
}

提交结果:

image.png

oh yeah! 击败了100%的用户,内存使用也更少了。

时间复杂度是O(mn)m是字符串数组中的字符串的平均长度,n是字符串的数量,空间复杂度是O(1),因为没有额外开辟空间。

解法2(纵向搜索)

还有一种解法是,数组中每个字符串按照位置一一比较,比如所有字符串0位置处是否相等,如果相等,继续比较所有后续位置,如果出现不相等,则对比结束,会得到最大公共前缀。

1.png
如图所有绿色的相等,然后所有蓝色的相等,后面不相等则结束比较。

代码如下:

class Solution {
    public String longestCommonPrefix(String[] strs) {
        String first = strs[0];
        for (int i = 0; i < first.length(); i++) {
            char c = strs[0].charAt(i); // 第一个字母,第i个位置的字符
            for (int j = 1; j < strs.length; j++) {
                if (i == strs[j].length() || c != strs[j].charAt(i)) {
                    return strs[0].substring(0, i);
                }
            }
        }
        return strs[0];
    }
}

提交:

image.png

这里要考虑字符串长度不一样的问题,i == strs[j].length()就是说如果达到了字符串的长度,那么就直接返回即可。
空间复杂度和时间复杂度跟解法1是一样的。
运行时间击败了70.07%的java用户,内存使用击败了67.59%用户,也不是很好呀,还有更好的解法吗?想了半天我也没想出来,看了下力扣的题解,看到了分治法二分查找法

解法3(分治法)

没想到分治算法,看了题解才知道,分治算法的思想很简单,就是将数据分片,单独计算,然后合并结果,如下图所示:

1.png

正如上图所示,将数据层层切分,当在最下层的时候开始计算左右两个字符串的最大公共前缀,以此类推,逐层向上计算,这也是递归的思想,先“递”后“归”,代码如下:

class Solution {
    public String longestCommonPrefix(String[] strs) {
        // 开始的时候传递整个数组
        return longestCommonPrefix(strs, 0, strs.length - 1);
    }

    public String longestCommonPrefix(String[] strs, int start, int end) {
        // 当start=end的时候,说明数字只有一个元素,一个元素的最大公共前缀就是它自己
        if (start == end) {
            return strs[start];
        }
        // 每次将数组拆分一半
        int mid = (end - start) / 2 + start;
        // 递归获取左边的最大公共前缀
        String lcpLeft = longestCommonPrefix(strs, start, mid);
        // 递归获取右边的最大公共前缀
        String lcpRight = longestCommonPrefix(strs, mid + 1, end);
        // 计算左右两个最大公共前后缀的最大公共前后缀
        return commonPrefix(lcpLeft, lcpRight);
    }

    public String commonPrefix(String lcpLeft, String lcpRight) {
        int minLength = Math.min(lcpLeft.length(), lcpRight.length());
        for (int i = 0; i < minLength; i++) {
            if (lcpLeft.charAt(i) != lcpRight.charAt(i)) {
                return lcpLeft.substring(0, i);
            }
        }
        return lcpLeft.substring(0, minLength);
    }
}

提交到力扣:

image.png

时间上击败了100%的java用户,空间复杂度上击败了50.28%的用户,从结果上看确实是性能提升了,时间复杂度依然是O(mn)m是字符串数组中的字符串的平均长度,n是字符串的数量,空间复杂度是O(mlogn),空间复杂度主要取决于递归调用的层数,层数最大为 logn,每层需要 m 的空间存储返回结果。

解法4(二分查找法)

二分查找法我也是没有想到,看了官方的解释才明白,具体什么意思呢?看如下说明:

要知道一点,最大公共前缀的长度一定是小于所有字符串中最小长度。
比如:

1.png

最小长度是minLength=4,那么最大公共前缀的长度一定是[0, minLength],所有可以通过[0, minLength]来实现二分查找,代码如下:

class Solution {
    public String longestCommonPrefix(String[] strs) {
        // 首先要找出最小长度值minLength
        int minLength = Integer.MAX_VALUE;
        for (String str : strs) {
            minLength = Integer.min(minLength, str.length());
        }
        // 二分查找
        int left = 0;
        int right = minLength - 1;
        while (left <= right) {
            int mid = (left + right) >> 1;
            if (isCommonPrefix(strs, mid + 1)) { // mid + 1是长度,也就是计算[0-mid]这个字符串。
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return strs[0].substring(0, left);
    }

    public boolean isCommonPrefix(String[] strs, int length) {
        String str0 = strs[0].substring(0, length);
        int count = strs.length;
        for (int i = 1; i < count; i++) {
            String str = strs[i];
            for (int j = 0; j < length; j++) {
                if (str0.charAt(j) != str.charAt(j)) {
                    return false;
                }
            }
        }
        return true;
    }
}

提交结果如下:

image.png

时间上击败了70.27%的java用户,内存消耗上则击败了30.27%的java用户。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值