题目

给定一个单词列表,我们将这个列表编码成一个索引字符串 S 与一个索引列表 A。

例如,如果这个列表是 ["time", "me", "bell"],我们就可以将其表示为 S = "time#bell#" 和 indexes = [0, 2, 5]。

对于每一个索引,我们可以通过从字符串 S 中索引的位置开始读取字符串,直到 "#" 结束,来恢复我们之前的单词列表。

那么成功对给定单词列表进行编码的最小字符串长度是多少呢?

示例:

输入: words = ["time", "me", "bell"]输出: 10说明: S = "time#bell#" , indexes = [0, 2, 5] 。

提示:

1 <= words.length <= 20001 <= words[i].length <= 7每个单词都是小写字母 。

https://leetcode-cn.com/problems/short-encoding-of-words

今天leetcode的每日一题的官方题解的python解法惊艳到我了,代码十分Pythonic,正好我也不太熟悉字典树和reduce的用法,学了一下:简单的来说就是:一句话实现字典树,一句话完成建树过程。

class Solution:    def minimumLengthEncoding(self, words: List[str]) -> int:        words = list(set(words)) #remove duplicates        #Trie is a nested dictionary with nodes created        # when fetched entries are missing        Trie = lambda: collections.defaultdict(Trie)        trie = Trie()

        #reduce(..., S, trie) is trie[S[0]][S[1]][S[2]][...][S[S.length - 1]]        nodes = [reduce(dict.__getitem__, word[::-1], trie)                 for word in words]

        #Add word to the answer if it's node has no neighbors        return sum(len(word) + 1                   for i, word in enumerate(words)                   if len(nodes[i]) == 0)

Trie = lambda: collections.defaultdict(Trie)这个循环嵌套字典是类似这样的效果{{{{}}}},意思是只要没有key的我们就返回一个空字典。其实字典树的本质就是循环嵌套字典。trie[word[-1]][word[-2]].........是写成这样了reduce(dict.__getitem__, word[::-1], trie)

下面给出@Lucien在leetcode题解下的评论解释关于Python字典树方法的解释:

我们需要一棵字典树,把所有word加入这棵树 找到所有叶子的高度和 一步步从最正常的写法走向Pythonic的解。

# 定义字典树中的一个节点class Node(object):    def __init__(self):        self.children={}class Solution:    def minimumLengthEncoding(self, words: List[str]) -> int:        words = list(set(words)) #需要去重,否则在之后计算“叶子高度”的时候会重复计算        trie=Node() #这是字典树的根        nodes=[] #这里保存着每个word对应的最后一个节点,比如对于单词time,它保存字母t对应的节点(因为是从后往前找的)        for word in words:            now=trie            for w in reversed(word):                if w in now.children:                    now=now.children[w]                else:                    now.children[w]=Node()                    now=now.children[w]            nodes.append(now)        ans=0        for w,c in zip(words,nodes):            if len(c.children)==0: #没有children,意味着这个节点是个叶子,nodes保存着每个word对应的最后一个节点,当它是一个叶子时,我们就该累加这个word的长度+1,这就是为什么我们在最开始要去重                ans+=len(w)+1        return ans

相信以上的解答大家可以看懂,那么就从Node开始简化。原先我们把Node声明为一个类,但这个类中只有一个字典,所以我们不如就直接用一个字典来表示节点,一个空字典以为着这是一个叶子节点,否则字典中的每一个元素都是它的一个孩子,上面的代码可以简化为:

class Solution:    def minimumLengthEncoding(self, words: List[str]) -> int:        words = list(set(words)) #需要去重,否则在之后计算“叶子高度”的时候会重复计算        trie={} #这是字典树的根        nodes=[] #这里保存着每个word对应的最后一个节点,比如对于单词time,它保存字母t对应的节点(因为是从后往前找的)        for word in words:            now=trie            for w in reversed(word):                if w in now:                    now=now[w]                else:                    now[w]={}                    now=now[w]            nodes.append(now)        ans=0        for w,c in zip(words,nodes):            if len(c)==0: #一个空字典,意味着这个节点是个叶子                ans+=len(w)+1        return ans

继续简化,我们不想在生成字典树时每次都判断“当前字典有没有这个键”,我们希望,有这个键,就返回它的值,否则返回一个空字典给我。很自然,我们需要用到defaultdict,它默认返回一个字典。但,只是返回一个普通字典吗?比如defaultdict(dict)? 不行,实际上它需要返回一个defaultdict,且这个defaultdict仍旧会递归地返回defaultdict。于是,递归地,我们定义这样一个函数,它返回一个defaultdict类型,且它的默认值是该类型本身。Trie = lambda: collections.defaultdict(Trie) ,注意,这里的Trie是一个函数,它返回一个defaultdict实例。有了它,我们创建字典树的过程就变成了:

nodes=[]Trie = lambda: collections.defaultdict(Trie)trie = Trie()for word in words:    now=trie    for w in word[::-1]:        now=now[w]    nodes.append(now)

更进一步,可以简化为

nodes=[]Trie = lambda: collections.defaultdict(Trie)trie = Trie()for word in words:    nodes.append(trie[word[-1]][word[-2]].........)

它就变成了

nodes = [reduce(dict.__getitem__, word[::-1], trie)                 for word in words]

先不管数组的推导式,单看数组的一项 reduce(dict.getitem, word[::-1], trie),reduce三个参数分别为:方法,可循环项,初始值。即它初始值是trie,按照word[::-1]的循环顺序,每次去执行方法dict.getitem,且将这个输出作为下次循环的输入,所以它就是trie[word[-1]][word[-2]].........的意思。

最后一步的sum很简单,只要大家明白nodes里存的是什么就很明显了。

另外附上标准的C++写法:

class TrieNode{    TrieNode* children[26];public:    int count;    TrieNode() {        for (int i = 0; i 26; ++i) children[i] = NULL;        count = 0;    }    TrieNode* get(char c) {        if (children[c - 'a'] == NULL) {            children[c - 'a'] = new TrieNode();            count++;        }        return children[c - 'a'];    }};class Solution {public:    int minimumLengthEncoding(vector<string>& words) {        TrieNode* trie = new TrieNode();        unordered_mapint> nodes;for (int i = 0; i int)words.size(); ++i) {string word = words[i];            TrieNode* cur = trie;for (int j = word.length() - 1; j >= 0; --j)                cur = cur->get(word[j]);            nodes[cur] = i;        }int ans = 0;for (auto& [node, idx] : nodes) {if (node->count == 0) {                ans += words[idx].length() + 1;            }        }return ans;    }};

字典树实现_leetcode之820. 单词的压缩编码 | python极简实现字典树相关推荐

  1. 820. 单词的压缩编码

    820. 单词的压缩编码 思路:reverse后排序,只需要比较相邻的字符串 class Solution { public:int minimumLengthEncoding(vector<s ...

  2. LeetCode 820. 单词的压缩编码(后缀树)

    文章目录 1. 题目 2. 解题 2.1 反转字符串+字符查找 2.2 后缀树 1. 题目 给定一个单词列表,我们将这个列表编码成一个索引字符串 S 与一个索引列表 A. 例如,如果这个列表是 [&q ...

  3. python语言应用 智慧树满分章节测试答案_2020知到答案 Python语言应用 最新智慧树满分章节测试答案...

    [问答题,简答题] 简述幼儿游戏的功能. [单选] 按照游戏的目的性,可以将幼儿游戏分为三种,其中不属于的是(). [单选] 由多个儿童一起进行同样的或类似的游戏,没有分工,也没有按照任何具体目标或结 ...

  4. 字典树实现_【Leetcode每日打卡】单词的压缩编码 Trie(字典树)入门

    一.前言(鸡汤(一段废..话..可以跳过啦)) 同学们好!没想到我这个小小的公众号破千粉啦,对于大佬们而言或许不值一提,但是对我而言是一个莫大的鼓舞!更加坚定了我持续输出优质内容的决心.希望我们都能每 ...

  5. def python语言对照表_如何用python,华丽实现字典树?

    文/IT可达鸭 图/IT可达鸭.网络 前言 上篇文章,我们用自定义数据结构实现了有序集合set.本文在此基础上,进一步加深,设计一个新的数据结构--字典树.有过参加过算法竞赛或做过数据检索的小伙伴,应 ...

  6. python英语字典程序-python如何制作英文字典

    本文实例为大家分享了python制作英文字典的具体代码,供大家参考,具体内容如下 功能有添加单词,多次添加单词的意思,查询,退出,建立单词文件. keys=[] dic={} def rdic(): ...

  7. python字典返回键值对列表_返回列表Python dict dictionaries Python 数据结构——字典 返回列表...

    最近研究返回列表,稍微总结一下,以后继续补充: 字典是比列表更先进的一种内置数据结构. "字典"就像现实中的字典一样,每一个单词对应好几个意思.在Python面里就是每一个键对应一 ...

  8. python之集合与字典

    01 一山不容二虎的集合 1.通俗来说,集合(set)是一个无序的不重复元素序列,就是一个用来存放数据的容器. 1)集合里面的元素是不可重复的: 如: s = {1,2,3,4,1,2,3} prin ...

  9. python中怎样向字典中添加值_python中向一个字典中加值

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 开发准备cas 的 python sdk 包含了用于访问和操作 cas 的所有 ...

最新文章

  1. 【技术交流】让我们来谈一谈多线程和并发任务
  2. activity 点击后传递数据给fragment_Fragment 的过去、现在和将来
  3. SAP Spartacus Storefront页面的page layout config
  4. 计算机怎么更改用户头像像,Win10系统电脑账户头像怎么改成系统默认状态?
  5. 工厂模式 构建者模式_实践中的构建者模式
  6. LeetCode425——Add Strings(两个字符串中的数字相加(十进制或二进制),输出字符串形式的结果)
  7. video4linux 安装,【精品博文】Video4linux 下视频编程的流程
  8. python小孩的报酬_孩子做了家务,家长该不该支付“酬劳”呢?
  9. 自制简单表单验证relative与absolute定位
  10. 7z命令行参数详解--python暴破压缩文件命令必备
  11. ESP32-IDF给FATFS添加长文件名支持,更改_USE_LFN以支持大于8.3格式的文件名
  12. Linux——CMake更新和安装
  13. 随机生成验证码(JAVA代码)
  14. 11.微信小程序图片操作总结
  15. WhatsApp即时翻译WhatsApp聊天自动翻译成中文
  16. android 客户端和服务端cookie,如何在Android客户端注入及清除Cookie教程
  17. 【高精】Oliver的成绩
  18. QQ “你可能收到一条假消息” ”曾经”是如何实现的?
  19. 消息推送配置url(服务器地址),设置推送url接口
  20. 阿里云二级域名配置-ssl证书包不安全问题记录

热门文章

  1. OpenCV-绘制简易直方图DrawHistImg
  2. linux内核网络基础,linux网络内核基础.doc
  3. 分数的计算机应用教案,计算机应用实训实验
  4. ubuntu下的常见12个命令---欢迎补充
  5. 求数组中最长连续递增子序列
  6. 怎么利用c 语言编程进行进制计算,编程达人 《汇编、C语言基础教程》第一章 进制1.1 进制的定义(连载)...
  7. C语言从链表中删除素数,Data Structures 之 链表
  8. 安装oracle12c之前,centos7安装oracle12c数据库的坑
  9. springboot项目层次结构_Springboot项目结构Springboot项目结构
  10. 判断包含字符当中包含小数点_Python|提取包含指定文本的行到一个文本文件(字符串或正则)...