【LeetCode】648.单词替换(前缀树多种解法,java实现)

87 阅读2分钟

题目

链接

image-20200628203432721

分析

方法一:前缀哈希【通过】

思路

遍历句子中每个单词,查看单词前缀是否为词根。

算法

将所有词根 roots 存储到集合 Set 中。遍历所有单词,判断其前缀是否为词根。如果是,则使用前缀代替该单词;否则不改变该单词。

class Solution {
    public String replaceWords(List<String> roots, String sentence) {
        Set<String> rootset = new HashSet();
        for (String root: roots) rootset.add(root);

        StringBuilder ans = new StringBuilder();
        for (String word: sentence.split("\\s+")) {
            String prefix = "";
            for (int i = 1; i <= word.length(); ++i) {
                prefix = word.substring(0, i);
                if (rootset.contains(prefix)) break;
            }
            if (ans.length() > 0) ans.append(" ");
            ans.append(prefix);
        }
        return ans.toString();
    }
}

复杂度分析

  • 时间复杂度: O ( ∑ w i 2 ) O(\sum w_i^2) O(∑wi2​),其中 $w_i $是第 i个单词的长度。检查第 i 个单词的所有前缀花费时间 O ( w i 2 ) O(w_i^2) O(wi2​)。
  • 空间复杂度:O(N),其中 N是句子的长度,词根使用 rootset 存储。

方法二:前缀树(数组)【通过】

思路和算法

把所有的词根放入前缀树中,在树上查找每个单词的最短词根,该操作可在线性时间内完成。

class Solution {
    public String replaceWords(List<String> roots, String sentence) {
        TrieNode trie = new TrieNode();
        for (String root: roots) {
            TrieNode cur = trie;
            for (char letter: root.toCharArray()) {
                if (cur.children[letter - 'a'] == null)
                    cur.children[letter - 'a'] = new TrieNode();
                cur = cur.children[letter - 'a'];
            }
            cur.word = root;
        }

        StringBuilder ans = new StringBuilder();

        for (String word: sentence.split("\\s+")) {
            if (ans.length() > 0)
                ans.append(" ");

            TrieNode cur = trie;
            for (char letter: word.toCharArray()) {
                if (cur.children[letter - 'a'] == null || cur.word != null)
                    break;
                cur = cur.children[letter - 'a'];
            }
            ans.append(cur.word != null ? cur.word : word);
        }
        return ans.toString();
    }
}

class TrieNode {
    TrieNode[] children;
    String word;
    TrieNode() {
        children = new TrieNode[26];
    }
}

复杂度分析

  • 时间复杂度:O(N),其中 NNsentence 的长度。每次查询操作为线性时间复杂度。
  • 空间复杂度:O(N),前缀树的大小。

方法三:前缀树(map)

//见到涉及单次前缀的 基本都是 用前缀树;
class Solution {
    public String replaceWords(List<String> dict, String sentence) {
        Trie trie = new Trie(dict,sentence); //穿件前缀树
        for(int i=0;i<dict.size();i++){ 
            trie.insert(dict.get(i),i+1); //将词根 放到前缀树
        }
        
          return trie.replace(); //执行替换 
    }
}
class TrieNode{
    char c;
    Map<Character,TrieNode> map = new HashMap<>();//这里我们用的是 map数据结构 而没有用数组,其实这两种都可以 
    int end;//定义当前的字符串 在列表中的位置 以1开始,为0说明这不是一个词;

    public TrieNode(char c){
        this.c = c;
    }
}

class Trie{
    TrieNode root;
    String sentence;
    List<String> list;
    public Trie(List<String> list,String s){
        root = new TrieNode('0');
        this.sentence = s;
        this.list = list;
    }
//插入逻辑比较简单。基本一个模子方式。 
    public void insert(String word,int index){
        TrieNode node = root;
        for(int i=0;i<word.length();i++){
            char cur = word.charAt(i);
            if(!node.map.containsKey(cur)){
                node.map.put(cur,new TrieNode(cur));
            }
            node = node.map.get(cur);
        }
        node.end = index;
    }
    public String search(String key){
        TrieNode node = root;
        for(int i=0;i<key.length();i++){
            char cur = key.charAt(i);
            if(node.end>0){ //如果 end大于0 说明 是 一个最短的完整词根 直接返回
                return list.get(node.end-1);
            }else{
               if(!node.map.containsKey(cur)){ //如果 没有  则 直接完整返回
                   return key;
               }else{
                  node = node.map.get(cur);  
               }
            }
            
        }
        return key;
    }

    public String  replace(){
        String[] ss = this.sentence.split(" ");
        StringBuilder sb = new StringBuilder();
        for(String s:ss){
            sb.append(search(s)).append(" ");
        }
        return sb.substring(0,sb.length()-1);
    }


}