这道题要求设计一个 WordDictionary,既能插入单词,又能支持带通配符 . 的搜索。'.' 可以匹配任意一个字母。leetcode
核心难点在于:如何在字典树中优雅地支持通配符搜索。geeksforgeeks+1
题目概述
题目接口如下:leetcode
- WordDictionary():初始化对象。
- void addWord(word):把 word 加入数据结构。
- bool search(word):如果存在任意已添加的字符串能和 word 匹配,则返回 true,否则返回 false;word 可能包含 '.',代表任意字母。
约束:单词长度不超过 25,search 中的 word 最多包含 2 个 '.',总调用次数最多 10^4。这些约束保证了基于 Trie + DFS 的解法可以轻松通过。geeksforgeeks+1
为什么选 Trie
Trie(前缀树)非常适合存储大量字符串并做前缀或精确匹配搜索。algocademy+1
- 插入一个单词:从根开始,按字符依次向下,如果没有对应子节点就创建,最后的节点标记为"单词结尾"。geeksforgeeks
- 普通搜索(无通配符):同样逐字符往下走,如果中途某个字符对应的子节点不存在,直接返回 false;如果能走到最后一个字符,并且当前节点是"单词结尾",返回 true。geeksforgeeks
在本题中,插入完全是标准 Trie 操作,难点都在搜索的 . 处理上。github+1
搜索逻辑:关键在 . 的处理
搜索函数的整体思路是:对字符串做 DFS 搜索。algo+1
设一个递归函数:
text
bool dfs(trie_node *node, const char *word, int idx)
表示"当前在 node 节点,从 word[idx] 开始匹配剩余字符串,能否成功"。algo
分两种情况:
-
当前字符是普通字母 c:
- 在当前节点的子节点中找到字符 c 对应的 child。
- 如果不存在,直接返回 false。
- 如果存在,递归到这个 child,索引 idx + 1。
-
当前字符是 '.':
- 无法确定下一步走哪条边,需要遍历当前节点的所有子节点。
- 对每一个子节点 child 调用 dfs(child, word, idx + 1)。
- 只要有一个返回 true,当前就可以返回 true;如果所有子节点都失败,返回 false。geeksforgeeks+1
当 idx 到达字符串末尾时,如果当前节点是一个单词结尾,返回 true,否则返回 false。algo
自定义 get_child_node 接口设计
你设计的接口大致是:
c
void get_child_node(trie_node_t *parent_node,
trie_node_t **children,
int *children_num,
char key);
行为约定为:
- 如果 key 不是 '.':
- 能找到对应子节点:children 返回该 child,children_num = 1。
- 找不到:children = NULL,children_num = 0。
- 如果 key 是 '.':
- children 返回"所有子节点"组成的链表头指针。
- children_num 为子节点个数。
在 search / dfs 里就可以这样用:
- 普通字符:get_child_node 只返回 0 或 1 个 child,直接判断是否存在并继续递归。
- '.':get_child_node 返回一个 child 链表,上层遍历这 children_num 个 child,对每个 child 递归匹配剩余子串。
这个设计本质上就是把"根据 key 拿到可能的 child 集合"的逻辑封装到一个函数里,和主流的 Trie + DFS 解法在思想上是完全一致的,只是具体 API 风格不同。okc1.github+1
DFS 还是 BFS?
在这题里,主流解法都是 DFS,原因有:
- 每条路径最多 25 个字符,递归深度很浅;DFS 写法简洁直观。github+1
- '.' 的分支数有限,DFS 回溯成本可控。
理论上也可以用 BFS:队列里存 (node, idx) 状态,每次出队一个状态,根据当前字符扩展到下一层节点;但代码会比 DFS 啸嗦一些,所以一般不作为首选。accidentalfactors+1
更一般地说,Trie 自身既可以 DFS 遍历,也可以 BFS 遍历:
- 自动补全需要"枚举某前缀下所有单词"时,大家通常用 DFS,但如果需要按长度层级排序,也可以选 BFS。ali-ibrahim137.github+1
小结
- 数据结构:用 Trie 存所有单词。
- 搜索策略:用 DFS 处理通配符 '.',在出现 '.' 的位置展开所有子节点分支。
- 接口设计:把"取子节点集合"的逻辑封装成 get_child_node,对于普通字符返回 0/1 个 child,对于 '.' 返回所有 child 链表,上层统一用 DFS 处理。
从算法本质上看,这种写法与 LeetCode 官方推荐的 "Trie + DFS" 解法是一致的,足以通过所有测试用例。leetcode+2
https://leetcode.com/problems/design-add-and-search-words-data-structure/