PracticeNeetCode ↗LeetCode ↗
Design a data structure that supports adding words and searching for a word, where a search pattern may contain . as a wildcard matching any single character.
addWord behaves exactly like trie insert.. can match any character, so a plain iterative walk isn't enough: when the pattern hits a ., every child at that node needs to be tried.. — a literal character still does a direct, O(1) child lookup, keeping the common case fast.is_end structure as a normal trie.dfs(node, i) that walks the search word starting from index i.node.children — missing means this path fails.., recurse into every child of node — the search succeeds if any branch returns True.i reaches the end of the word, return the node's is_end flag.Time: O(L) with no wildcards, up to O(26^L) worst case when every character is a wildcard — Space: O(N · L) for the trie, O(L) recursion depth
class TrieNode:
def __init__(self):
self.children: dict[str, "TrieNode"] = {}
self.is_end = False
class WordDictionary:
def __init__(self):
self.root = TrieNode()
def addWord(self, word: str) -> None:
node = self.root
for ch in word:
if ch not in node.children:
node.children[ch] = TrieNode()
node = node.children[ch]
node.is_end = True
def search(self, word: str) -> bool:
def dfs(node: TrieNode, i: int) -> bool:
if i == len(word):
return node.is_end
ch = word[i]
if ch == ".":
return any(dfs(child, i + 1) for child in node.children.values())
if ch not in node.children:
return False
return dfs(node.children[ch], i + 1)
return dfs(self.root, 0)How the solution above works, piece by piece.
addWord is a normal trie insert.search runs dfs(node, i): "can word[i:] be matched starting from node?"i == len(word), the match succeeds only if this node ends a word.. matches any character: try every child with any(...), which stops at the first success.Running the example calls:
NeetCode — Design Add and Search Words Data Structure, LeetCode 211