zoukankan      html  css  js  c++  java
  • 【LeetCode】648.单词替换(前缀树多种解法,java实现)

    题目

    链接

    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(wi2)O(sum w_i^2),其中 $w_i $是第 i个单词的长度。检查第 i 个单词的所有前缀花费时间 O(wi2)O(w_i^2)
    • 空间复杂度: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);
        }
    
    
    }
    
  • 相关阅读:
    linux 6 安装 Nagios服务
    linux 6 安装 Nginx服务
    Rsync的配置与使用
    linux 6 搭建 msyql 服务
    linux6搭建Apache服务
    Linux 7搭建NFS服务
    Linux 6 忘记root密码重置
    简单makefile
    多线程c++11编程题目
    redis 代码结构与阅读顺序
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13308010.html
Copyright © 2011-2022 走看看