Leetcode 14. 最长公共前缀

题目

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

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

示例 1:

输入: [“flower”,“flow”,“flight”]
输出: “fl”

示例 2:

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

说明:

  • 所有输入只包含小写字母 a-z 。

解答

解法一:利用字典序

利用字符串排序时的字典序规则。

排序后,最小字符串和最大字符串的公共前缀即是所有字符串的公共前缀。

复杂度:O(m * nlogn) 的时间,O(1) 的空间

代码
class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length < 1) return "";
        Arrays.sort(strs);
        String min = strs[0];
        String max = strs[strs.length - 1];
        StringBuilder stb = new StringBuilder();
        for(int i = 0; i < min.length(); i++) {
            if(max.charAt(i) == min.charAt(i)) {
                stb.append(max.charAt(i));
            } else {
                break;
            }
        }
        
        return stb.toString();
    }
}
结果

在这里插入图片描述

解法二:水平扫描

具体如下:

  1. 让公共前缀初始化为第一个字符串。
  2. 然后不断的向后迭代,不断地缩减公共前缀 prefix 的长度。

复杂度:O(m * n) 的时间,O(1) 的空间。

代码
class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length == 0) return "";
        
        String prefix = strs[0];
        for(int i = 1; i < strs.length; i ++) {
            String str = strs[i];
            while(str.indexOf(prefix) != 0) {
                prefix = prefix.substring(0, Math.min(str.length(), prefix.length() - 1)); 
            }
        }
        
        return prefix;
    }
}
结果

在这里插入图片描述

解法三:垂直扫描

垂直扫描上述思路略有不同,垂直扫描比较的是每个字符串相同位置的字符是否相同。

从第一个位置开始迭代,一旦发现有任何一个字符串在当前位置的字符与其他的不同,那么此位置就是最大前缀的边界点。

复杂度:O(m * n) 的时间,O(1) 的空间。

代码
class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length == 0) return "";
        
        StringBuilder res = new StringBuilder();
        for(int i = 0; i < strs[0].length(); i ++) {
            char ch = strs[0].charAt(i);
            for(int j = 1; j < strs.length; j ++) {
                String str = strs[j];
                if(i >= str.length() || str.charAt(i) != ch) {
                    return res.toString();
                }
            }
            
            res.append(ch);
        }
        
        return res.toString();
    }
}
结果

在这里插入图片描述

解法四:二分法

类似于解法二,不同的是公共前缀的确定使用了二分法,每次都对当前的前缀长度加倍或减半。

主要思路:

  1. 先找到最短的字符串长度,因为最大公共前缀的长度不会超过最短的字符串长度 minLen 。
  2. 采用二分法:
    1. 如果当前的前缀是所有字符串的公共前缀,那么就尝试将当前的前缀长度加倍。
    2. 否则,让当前的前缀长度减半。
  3. 最后返回由左右边界确定的公共前缀即可。

复杂度:O(m * nlogn) 的时间,O(1) 的空间。

代码
class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length < 1) return "";
        
        int minLen = strs[0].length();
        for(int i = 1; i < strs.length; i ++) {
            minLen = Math.min(minLen, strs[i].length());
        }

        int low = 1;
        int high = minLen;
        while(low <= high) {
            int mid = (low + high) >>> 1;
            if(isCommonPrefix(strs, mid)) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        
        return strs[0].substring(0, (low + high) >>> 1);
    }
    
    private boolean isCommonPrefix(String[] strs, int len) {
        String prefix = strs[0].substring(0, len);
        for(int i = 1; i < strs.length; i ++) {
            if(!strs[i].startsWith(prefix)) return false;
        }
        
        return true;
    }
}
结果

在这里插入图片描述

解法五:分治法

采用归并排序的思想,不断融合字符串的公共前缀。

复杂度:O(m * n) 的时间,O(m * logn) 的空间。

代码
class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length == 0) return "";
        return merge(strs, 0, strs.length - 1);        
    }
    
    private String merge(String[] strs, int start, int end) {
        if(start == end) return strs[start];
        
        int mid = (start + end) >>> 1;
        String left = merge(strs, start, mid);
        String right = merge(strs, mid + 1, end);
    
        return mergePrefix(left, right);
    }

    private String mergePrefix(String s1, String s2) {
        if(s1.equals(s2)) return s1;
    
        int minLen = Math.min(s1.length(), s2.length());
        for(int i = 0; i < minLen; i ++) {
            if(s1.charAt(i) != s2.charAt(i)) return s1.substring(0, i); 
        }
        
        return s1.substring(0, minLen);
    }
}
结果

在这里插入图片描述

扩展解法:Trie 字典树

使用 Trie 数据结构,将单词存储在一颗多叉树上。

对 Trie 不太了解的同学可以看一下:Leetcode 208. 实现 Trie (前缀树)

对于本题,需要注意所有字符串的公共前缀必须满足的条件:

  1. 当前结点的 next 数组有效长度必须为 1 。(不能有分叉)
  2. 当前结点不是某一个字符串的结束点。(当前结点不是单词的结束结点)

复杂度:O(m * n) 的时间,O(m * n) 的空间。

代码
class Solution {
    
    class Node {
        boolean isWord;
        int size;
        Node[] next = new Node[26];
    }
    
    private Node root = new Node();
    
    public String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length == 0) return "";
        
        for(int i = 0; i < strs.length; i ++) {
            if(strs[i].length() == 0) return "";
            addWord(strs[i]);
        }
        
        return getLongestPrefix(strs[0]);
    }
    
    private void addWord(String word) {
        Node p = root;
        for(int i = 0; i < word.length(); i ++) {
            char ch = word.charAt(i);
            if(p.next[ch -'a'] == null) {
                p.next[ch - 'a'] = new Node();
                p.size ++;
            }
            
            p = p.next[ch - 'a'];
        }
        
        p.isWord = true;
    }
    
    private String getLongestPrefix(String word) {
        Node p = root;
        for(int i = 0; i < word.length(); i ++) {
            char ch = word.charAt(i);
            if(p.size != 1 || p.isWord) {
                return word.substring(0, i);
            } else {
                p = p.next[ch - 'a'];
            }
        }
        
        return word;
    }
}
结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
字符串数组中的最公共前缀可以通过遍历数组中的字符串进行比较来找到。我们可以设定一个起始的字符串,将其与后续的字符串进行比较,看是否存在公共前缀。如果存在公共前缀,则将起始字符串替换为该公共前缀,再与后续的字符串进行比较,直到遍历结束,最后得到的字符串就是最的公共前缀。\[1\] 以下是一个使用Java语言实现的示例代码: ```java public String longestCommonPrefix(String\[\] strs) { if (strs.length == 0 || strs == null) { return ""; } String ret = strs\[0\]; for (int i = 1; i < strs.length; i++) { if (ret.length() == 0) { return ""; } ret = stringReturn(ret, strs\[i\]); } return ret; } public String stringReturn(String s1, String s2) { int length = Math.min(s1.length(), s2.length()); int i = 0; while (i < length && s1.charAt(i) == s2.charAt(i)) { i++; } return s1.substring(0, i); } ``` 在上述代码中,我们首先判断字符串数组的合法性,然后设定起始字符串为数组的第一个元素。接着通过循环遍历数组中的字符串,将起始字符串与当前字符串进行比较,得到公共前缀。最后返回最的公共前缀。\[2\]\[3\] 请注意,如果字符串数组为空或度为0,则返回空字符串。如果没有找到公共前缀,则返回空字符串。 #### 引用[.reference_title] - *1* *2* *3* [《LeetCode系列》---最公共前缀](https://blog.csdn.net/weixin_50584850/article/details/124648635)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值