leetcode 820.单词的压缩编码 python set的discard方法 字典树Trie模板

题目描述

在这里插入图片描述

题解

解法一

刚开始的思路是,循环每个单词,和#一起拼接成字符串s,同时用一个列表记录下#的位置。当循环到新的一个单词时,就检查每个#前与当前单词同样长度的字符串,看这个字符串和当前单词是否一致。如果一致则字符串s不改变,如果不一致则字符串s添加新的单词。

这种方法在[‘me’,‘time’]时出错了,正确的答案是 time#,但是按上面的思路,得到的答案是 me#time#。

新的方法:一个列表s用于存储倒序的单词,然后循环words里的单词,与s中的每一个单词对比,根据两个单词的长度分情况对比。如果长度相等,则对比单词是否相等,相等则break退出循环;如果s中的单词s[j]的长度jl比当前单词的长度wl长,则截取s中的单词s[j]一定的长度与word对比;反之,截取word单词与s[j]对比,如果word单词的末尾包含了单词s[j],则将s[j]从列表s中删去,添加word。

好的,运行,超时了…

于是加了一段 if s[j][0] != word[0]: # 防超时,先直接判断一个字母,不要直接一上来就判断整个单词…
勉强过关了,但是耗时还是很长…

class Solution:

    def minimumLengthEncoding(self, words: List[str]) -> int:
        s = []
        s.append(str(words[0][::-1]))

        for i in range(1,len(words)):
            word = words[i][::-1]
            for j in range(len(s)):
                jl = len(s[j])
                wl = len(word)
                if s[j][0] != word[0]: # 防超时
                    continue
                if s[j] == word:
                    break
                if jl > wl:
                    if word == s[j][:wl]:
                        break
                if wl > jl:
                    if word[:jl] == s[j]:
                        s.pop(j)
                        s.append(word)
                        break
            else:
                s.append(word)
                
        return sum(len(word) + 1 for word in s)

解法二 set的discard

让我们来看看官方代码吧

class Solution:
    def minimumLengthEncoding(self, words: List[str]) -> int:
        good = set(words)
        for word in words:
            for k in range(1, len(word)):
                good.discard(word[k:])

        return sum(len(word) + 1 for word in good)

首先是使用了集合set(),使用它的好处有两个,一个是防止单词重复,二是删除单词的效率很高。

discard是删除的函数,discard与remove的区别。重点!!!后面要考的
在这里插入图片描述

然后它的思路是这样的,先把所有单词都存入good里,然后每个单词循环它的后缀,也就是从后缀为最后一个字母一直到后缀为整个单词除去第一个字母,然后在good里删除所有后缀。太暴力了!!

举例,good = {me, bell, time},现在word循环到time了,time的后缀应该循环e,me,ime这3个,然后在good里删后缀。这里discard的第二个好处体现了,就算good里没有e,执行discard也不会出错。当执行到删除me时,good里原来的me就被删除了。

同时,最后return的写法也值得借鉴,很简洁很pythonic…

解法三 字典树 Trie

还有一个字典树的trie,这也是看了别人题解才懂的,本菜鸡还是第一次听到这个东西…
没事,有第一次就有第二次,我们总会熟络起来的。
这里参考了 fe-lucifer的题解

前缀树的 api 主要有以下几个:

  • insert(word): 插入一个单词
  • search(word):查找一个单词是否存在
  • startWith(word): 查找是否存在以 word 为前缀的单词
class Trie:

    def __init__(self): # 初始化
        """
        Initialize your data structure here.
        """
        self.Trie = {}

    def insert(self, word): # 插入一个单词 word
        """
        Inserts a word into the trie.
        :type word: str
        :rtype: void
        """
        curr = self.Trie  # 将字典赋给curr
        for w in word:  # 循环每个字母
            if w not in curr: # 如果字母不在字典中
                curr[w] = {}  # 新建一个字典,用于存储这个w之后的字母
            curr = curr[w]  # 赋值给curr,这样下一个字母就会继续嵌套在字典的字典里
        curr['#'] = 1 # 最后一个字母作为key,它的value本来是个{},现在使value='#'

    def isTail(self, word): # 看word在不在trie中
        """
        Returns if the word is in the trie.
        :type word: str
        :rtype: bool
        """
        curr = self.Trie
        for w in word:
            curr = curr[w]  # 随着字母的循环,一直走入嵌套的字典中
        return len(curr) == 1 # 因为最后字母的value是#,
        # 所以长度是1.如果Trie中没有这个词,那就没有最后的#
        
class Solution:
    def minimumLengthEncoding(self, words: List[str]) -> int:
        trie = Trie()  #  初始化一个字典树
        cnt = 0  # 记录长度
        words = set(words)  # 将words转为set,去掉重复的单词
        for word in words:
            trie.insert(word[::-1])  # 倒序插入字典树中,因为是要判断后缀
            # 看trie.insert的函数构造,可以看出,如果这个单词是另一个单词的后缀,
            # 那它是不会成为一条新的分支的,也就是说 判断它isTail是false的,因为要len==1
            
        for word in words:
            if trie.isTail(word[::-1]): # 看这个单词在不在字典树中
                cnt += len(word) + 1
        return cnt


作者:fe-lucifer
链接:https://leetcode-cn.com/problems/short-encoding-of-words/solution/python3-hou-zhui-shu-mo-ban-ti-820-dan-ci-de-ya-su/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值