您的位置:首页 > 其它

Leetcode 14. 最长公共前缀

2019-07-19 14:53 363 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/LetJava/article/details/96477505

题目

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

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

示例 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. 采用二分法: 如果当前的前缀是所有字符串的公共前缀,那么就尝试将当前的前缀长度加倍。
  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
    4000
    ) 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;
    }
    }

    结果

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: