2020/3/28 打卡
题目
给定一个单词列表,我们将这个列表编码成一个索引字符串 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] 。
思路
这里首先理解题意, 是针对indexes中指定的索引位置读起,一直读到#位置,针对于示例,其读取过程是:
(1)从0索引的t开始读, 一直读到#位置,第一次读到的单词是 time
(2)从2索引的 m开始,一直读到#位置,读到的单词是 me
(3)从5索引的 b开始,一直读到#位置,读到的单词是 bell
明白了吧,这里使用尽量短的S 来实现上面的索引压缩查找效果。
解法上撕下字典树(trie树)吧
用递归的方式 构造字典树,对于每个单词从从后往前处理。
最后答案是每个叶子节点的层数的和(编码字符串中的单词长度) + 叶子节点数量(#数量)
代码
class Solution:
def minimumLengthEncoding(self, words):
class Node:
def __init__(self, l):
# 层数
self.l = l
# children是对应的孩子。 对应树中的分叉,分成多个孩子
self.children = {}
root = Node(0)
########################## 递归构建前缀树 ###################
# 递归构造字典树 递归构建的过程,不断加入新词w (注意这里是将单词的最后一个字母,从后往前构建的树,)
def build(t, w):
if not w:
return
# 把最后一个字母作为树的分叉孩子, 声明层数,并创建了一个树结点
if w[-1] not in t.children:
t.children[w[-1]] = Node(t.l + 1)
# 构建树时,一个一个字母的放置构建,
build(t.children[w[-1]], w[:-1])
# 对于 单词数组,逐步构建
for w in words:
build(root, w)
########################## 针对构建好的树,算出拼成所有单词的最短长度 ###################
# 对应的计算方式可以看题解中的 图,就是那个5+4+3+5=17的计算过程,比较简单。 https://leetcode-cn.com/problems/short-encoding-of-words/solution/dan-ci-de-ya-suo-bian-ma-by-leetcode-solution/
# 相当于全局变量,以便在递归中累加
ans = [0]
# 计算答案
def vis(t):
# 是叶子节点 只在叶子节点处,统计长度的累加,利用高度做累加。
if len(t.children) == 0:
if t.l > 0:
# 累加
ans[0] += t.l + 1
# 非叶子节点,就直接继续往下走
for c in t.children.values():
vis(c)
vis(root)
return ans[0]