美文网首页
『字典树』单词的压缩编码820

『字典树』单词的压缩编码820

作者: iamlightsmile | 来源:发表于2020-03-28 17:32 被阅读0次

    题目相关

    题目解读

    经过简单分析可知,如果能够实现编码复用,必须得满足某个单词是另一个较长单词的后缀才可以。很明显,我们可以通过构造字典树来实现。

    Python相关

    见下面

    具体实现

    自己的实现方案:

    class Solution:
        def minimumLengthEncoding(self, words: List[str]) -> int:
            res = []
            dic = {}
            for word in words:
                temp = dic
                for letter in word[::-1]:
                    temp = temp.setdefault(letter, {})
            def helper(node, length):
                if not node:
                    res.append(length + 1)
                else:
                    for node in node.values():
                        helper(node, length + 1)
            helper(dic, 0)
            return sum(res)
    

    官方给出的更加Pythonic的实现

    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)
    trie = Trie()
    

    这两行,不得不说很Pythonic,这里Trie是一个函数,是通过lambda匿名函数表达式实现的,而后面的collections.defaultdict中的构造函数也是Trie,就有点递归函数嵌套定义的意思了。总之,这两行的意思是构造一个能构造defaultdict对象的函数,并且该对象的默认属性值也是一个defaultdict对象。然后通过函数调用实例化一个对象trie
    后面是:

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

    不得不承认这句更加Pythonic,该句的意思是对于words中的word,都将其逆序对tire对象进行构造,其中reduce函数接收一个函数,一个迭代器,以及一个可选的初始值,功能是:

    函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

    现在我们看一个示例:

    a = {'b': 3}
    print(dict.__getitem__(a, 'b'))
    

    输出结果自然是3,因为此时的调用dict.__getitem__(a, 'b')完全等价于调用a['b']。而defaultdict是dict的子类,自然也有此性质。
    假设word为'love'的话,那么reduce(dict.__getitem__, word[::-1], trie)语句的执行流程就是先执行trie['e']语法糖,假设trie此时没有'e'这个item,,那么便会执行类似trie['e'] = Trie()trie['e'] = collections.defaultdict(Trie)类似的操作,并返回trie['e']这个对象,然后再执行trie['e']['v'] = Trie()trie['e']['v']['o'] = Trie()trie['e']['v']['o']['l'] = Trie()这一系列操作,最终的返回结果则是trie['e']['v']['o']['l']这个对象。
    因此:

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

    语句的执行结果,则是nodes中存储的都是一系列defaultdict(Trie)对象,如果有的单词是另外单词的后缀,那么此时对应的对象则不为空,否则为空,对其调用len函数的结果是0。
    因此,我们还可以将代码改写为:

    class Solution:
        def minimumLengthEncoding(self, words: List[str]) -> int:
            words = list(set(words)) 
    
            Trie = lambda: collections.defaultdict(Trie)
            trie = Trie()
    
            nodes = [functools.reduce(dict.__getitem__, word[::-1], trie)
                     for word in words]
    
            return sum(len(word) + 1
                       for node, word in zip(nodes, words)
                       if not node)
    

    参考

    1. python - Any Functional Programming method of traversing a nested dictionary? - Stack Overflow
    2. python3 Tire solution - LeetCode Discuss

    相关文章

      网友评论

          本文标题:『字典树』单词的压缩编码820

          本文链接:https://www.haomeiwen.com/subject/sihsuhtx.html