InterviewPrepKit

Home / Coding / Tries

Design Add and Search Words Data Structure

medium Original ↗
Solving tips
  • This is 'Implement Trie' plus a wildcard: build a trie for addWord, then search with a DFS that fans out on '.'.
  • At a literal character descend into that one child; at '.' recurse into every child and succeed if any branch matches.
  • '.' matches exactly one letter (not regex .*), so the query length must equal the word length; the base case at i==len(word) must check is_end.
  • addWord is O(L); a dot-free search is O(L), and each dot multiplies branching by up to 26, so d dots cost O(26^d * L) (d <= 2 here).

Problem

Design a container for lowercase words that supports:

  • WordDictionary() — create an empty container.
  • addWord(word) — store word.
  • search(word) — return True if any stored word matches word, where word may contain the wildcard character '.'. A '.' matches exactly one arbitrary letter; every other character must match literally. The match must cover the whole word (same length).

So after adding "bad", the query "b.d" matches, ".ad" matches, but "b." does not (wrong length) and "b.dx" does not.

Examples

  • addWord("bad"), addWord("dad"), addWord("mad"); then search("pad")False — no stored word is "pad".
  • search("bad")True (exact match); search(".ad")True — the dot can stand for b, d, or m.
  • search("b..")True"bad" matches with . standing for a then d; search("..")False — all stored words have length 3.

Constraints

  • 1 <= len(word) <= 25 for both added and searched words.
  • Added words contain only lowercase letters; search queries contain lowercase letters and '.'.
  • There are at most 2 '.' characters in any single search query.
  • At most 10^4 calls total to addWord and search.

Think about it first

Hint 1 Without dots this is exactly "Implement Trie". What breaks when a '.' shows up mid-walk?
Hint 2 A '.' means you don't know which child edge to follow — so follow all of them. That turns the single-path walk into a branching search.
Hint 3 Write a recursive matcher dfs(node, i): if word[i] is a letter, descend into that one child; if it is '.', try every child and succeed if any branch does. At i == len(word), succeed iff node.is_end.
Your workspace Not runnable by design — this is your interview scratchpad. Saved on this device.