Problem Statement
Design Add and Search Words Data Structure
We need to build a small system that does two jobs. First, it can store words we hand it. Second, it can answer the question "is this word in storage?" The twist is that a search word may contain a dot '.', and a dot is a wildcard that matches any single letter. So searching for ".ad" should find "bad", "dad", or "mad", because the dot stands in for any one letter. The tool that fits this is a trie. A trie (say "try") is like a branching tree of letters, similar to how a dictionary groups words by their first letter, then their second, and so on. Each step down the tree is one letter, and shared beginnings share the same branch. A trie makes letter-by-letter lookup natural, and it gives us an easy place to branch out when we hit a wildcard dot.
Signals to notice
Brute force first
Store words in a list, for each search check every word with regex — per search. That instinct is useful because it follows the prompt literally, but it usually keeps revisiting work the problem is begging you to organize.
The key insight
Trie for addWord. For search: if the character is a letter, follow that branch. If it's '.', try ALL children branches (DFS). A word exists if any branch reaches an end-marked node. worst case, but typically much faster. The goal is not to be clever for its own sake, but to remember the one relationship that keeps the solution grounded as you move forward.
Trace it on ops: addWord("bad"), addWord("dad"), addWord("mad"), then search("pad"), search("bad"), search(".ad"), search("b..")
addWord("bad"),("dad"),("mad"): root.children={b,d,m}; each path ->a->d with end=True at leaves
search("pad"): dfs(0,root), 'p' not in {b,d,m} -> return False
search("bad"): follow b->a->d, i==3 at d-node, d.end=True -> return True
search(".ad") i=0: '.' -> try children b,d,m via dfs(1,*)
branch b: 'a' in b.children -> dfs(2): 'd' in -> dfs(3): end=True -> True
search(".ad") returns True (b-branch succeeded, no need to try d,m)
search("b.."): follow b (exact), then '.'->a, then '.'->d, i==3 end=True -> return True
final outputs: [False, True, True, True]What must stay true
The '.' wildcard means you must explore all possible branches at that position. This turns a simple trie walk into a DFS — but the trie still prunes impossible paths at non-wildcard positions. If that remains true after every update, the rest of the reasoning has a stable place to stand.
Shape of the loop
addWord(w): node=root; for c in w: node=node.children.setdefault(c); node.end=True search(w): return dfs(0, root) dfs(i, node): if i==len(w): return node.end if w[i]=='.': return any(dfs(i+1, ch) for ch in node.children.values()) return w[i] in node.children and dfs(i+1, node.children[w[i]])
Pseudocode only — the full worked solution lives in the Solution tab.
Easy way to go wrong
Trying to search iteratively — the wildcard requires branching, which needs recursion or a stack. Iterative search only works for exact character matches. Most mistakes here are not about syntax; they come from losing track of what your state, pointer, or structure is supposed to mean.