当前位置: 首页 > news >正文

【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 ( ∑ 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);
    }


}

相关文章:

  • 【LeetCode】421. 数组中两个数的最大异或值(哈希集合,字典树,详细图文解释)
  • 【LeetCode】200.岛屿数量(dfs+bfs+并查集,超详细图文解释)
  • python实现强智科技教务系统抢课(两种方法)
  • [ChromeApp]指南!让你的谷歌浏览器好用十倍!
  • 【LeetCode】279.完全平方数(四种方法,不怕不会!开拓思维)
  • 【LeetCode】739.每日温度(5种方法,详细图解)
  • 【LeetCode】733.图像渲染(深度优先搜索,java实现)
  • 【LeetCode】56.合并区间(贪心算法,java实现)
  • 【LeetCode】旋转矩阵(原地选择+翻转两种方法,java实现)
  • 计算机基础(一):二进制详解
  • 计算机基础(二):压缩算法
  • 计算机基础(四):控制硬件
  • 【LeetCode】5.最长回文子串(中心扩散法,动态规划,超详细图文,java实现)
  • 【LeetCode】151.翻转字符串里的单词(三种方法,java实现)
  • 【LeetCode】28.实现strstr() (KMP超详细讲解,sunday解法等五种方法,java实现)
  • [ JavaScript ] 数据结构与算法 —— 链表
  • [译]前端离线指南(上)
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • Angular 2 DI - IoC DI - 1
  • CSS实用技巧干货
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • Intervention/image 图片处理扩展包的安装和使用
  • maven工程打包jar以及java jar命令的classpath使用
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • Objective-C 中关联引用的概念
  • Odoo domain写法及运用
  • Spring Cloud中负载均衡器概览
  • yii2中session跨域名的问题
  • 编写高质量JavaScript代码之并发
  • 分类模型——Logistics Regression
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 中文输入法与React文本输入框的问题与解决方案
  • 积累各种好的链接
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​力扣解法汇总946-验证栈序列
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • ​学习一下,什么是预包装食品?​
  • #微信小程序:微信小程序常见的配置传旨
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (八)Flask之app.route装饰器函数的参数
  • (理论篇)httpmoudle和httphandler一览
  • (算法)Game
  • (译)计算距离、方位和更多经纬度之间的点
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)http-server应用
  • (转)视频码率,帧率和分辨率的联系与区别
  • .bat批处理(六):替换字符串中匹配的子串
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .net项目IIS、VS 附加进程调试
  • .sdf和.msp文件读取
  • @JoinTable会自动删除关联表的数据
  • [ linux ] linux 命令英文全称及解释