0211_Design-Add-and-Search-Words-Data-Structure【M】Trie 树升级版(查找单词时支持 “.“ 的模糊匹配查找)

  • JY:Trie 树升级版(查找单词时支持 "." 的模糊匹配查找)

1、Trie 基础版(基于字典实现)

class TrieNode:
    def __init__(self):
        self.end_of_word = False
        self.children = {}


class WordDictionary:
    def __init__(self):
        # jy: 初始化 Trie 树的根节点
        self.root = TrieNode()

    def addWord(self, word: str) -> None:
        """
        往 Trie 树中添加 word
        """
        current = self.root
        for c in word:
            if c not in current.children:
                current.children[c] = TrieNode()
            current = current.children[c]
        current.end_of_word = True

    def search(self, word: str) -> bool:
        """
        从 Trie 树的根节点搜索单词 word (支持 "." 模糊匹配任一字符)
        """
        # jy: 递归函数中支持传入两个参数, 从哪个节点位置开始查找
        return self._search(word, self.root)

    def _search(self, word, node):
        """
        从 Trie 树的 node 节点开始搜索 word (支持 "." 模糊匹配任一字符)
        """
        current = node
        # jy: 由于后续可能需要跳过当前字符 c, 因此需要知道当前字符在 word
        #     中的下标位置 i, 随后可通过下标 i+1 开始表示跳过当前字符
        for i, c in enumerate(word):
            # jy: 深度优先搜索: 如果当前搜索字符为 "." (表示可以匹配任一字符), 则跳过当前
            #     字符 (即 key 值), 从下一个字符 (对应的节点即 current_child) 开始搜索
            if c == '.':
                for key, current_child in current.children.items():
                    if self._search(word[i+1:], current_child):
                        return True
                return False
            # jy: 如果搜索字符不为 ".", 且不在当前节点的子节点中, 则返回 False;
            #     注意: 必须先确保字符 c 不为 "." 后才进行当前判断, 否则直接进行
            #     当前判断时, 如果碰到的的字符 "." 会直接返回 False, 不符合预期
            elif c not in current.children:
                return False
            
            # jy: current 更新为当前节点的下一节点, 进行下一轮循环, 判断下
            #     一个搜索字符是否在 Trie 树的下一相邻层子节点中
            current = current.children[c]
            
        # jy: 经过以上 for 循环后, current 为 word 最后一个字符对应的 TrieNode,
        #     如果其 end_of_word 属性为 Ture, 表明该单词存在
        return current.end_of_word


wordDictionary = WordDictionary()
wordDictionary.addWord("bad")
wordDictionary.addWord("dad")
wordDictionary.addWord("mad")
print(wordDictionary.search("pad"))   # return False
print(wordDictionary.search("bad"))   # return True
print(wordDictionary.search(".ad"))   # return True
print(wordDictionary.search("b.."))   # return True

2、双字典

  • 时间复杂度略有提升,空间复杂度极佳
class WordDictionary:
    def __init__(self):
        # jy: 记录单词以及单词的长度
        self.word_len = {}
        # jy: 记录指定长度的单词列表
        self.len_words = defaultdict(list)

    def addWord(self, word: str) -> None:
        # jy: 如果单词 word 已经存在于 self.word_len 字典中, 表明
        #     之前已经添加过, 不需要重复添加; 否则基于单词长度添加到
        #     self.len_words 和 self.word_len 中
        if word not in self.word_len:
            len_ = len(word)
            self.len_words[len_].append(word)
            self.word_len[word] = len_

    def search(self, word: str) -> bool:
        # jy: 如果单词在 self.word_len 字典中, 直接返回 True
        if word in self.word_len:
            return True

        # jy: 只有当 word 中含 "." 时, 当前单词 w 才可能与 word 匹配 
        #     (否则不可能匹配, 因为 word 不在 self.word_len 中)
        if "." not in word:
            return False

        # jy: 从与 word 长度相同的候选列表中查找
        for w in self.len_words[len(word)]:
            # jy: length 从左到右记录 w 与 word 能相互匹配的字符个数
            length = 0
            for i in range(len(w)):
                if w[i] == word[i] or word[i] == '.':
                    length += 1
                else:
                    break
                    
            if length == len(w):
                return True
        return False


wordDictionary = WordDictionary()
wordDictionary.addWord("bad")
wordDictionary.addWord("dad")
wordDictionary.addWord("mad")
print(wordDictionary.search("pad"))   # return False
print(wordDictionary.search("bad"))   # return True
print(wordDictionary.search(".ad"))   # return True
print(wordDictionary.search("b.."))   # return True
  • 17
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

融码一生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值