◀ THE GRIND — TRIES

Design Add And Search Words Data Structure

MEDIUM✓ CHIP-TIMEDLC #211 — FULL STATEMENT ↗

The drill: A word set with a wildcard twist — add words, then ask whether a query matches one, where a dot in the query stands for exactly one unknown letter. The query's length still has to match a stored word's length exactly.

THE BRIEFING — THE FULL DRILL, IN MY OWN WORDS

Build a word store with a wildcard twist on lookup: add words one at a time, then answer whether a query string matches some added word, where a dot character in the query stands for exactly one unknown letter.

A match requires the query and the stored word to be the same length — a dot fills in for one letter, it never stands for zero letters or more than one — and every non-dot character in the query must match the stored word exactly at that position.

Multiple dots can appear in the same query, each one independently free to match any letter, as long as some single stored word satisfies every position at once.

EX 01
WordDictionary()
addWord("run")
addWord("fun")
addWord("sun")
search("bun") → false
search("run") → true
search(".un") → true
search("r.n") → true
search("ru.") → true
search("....") → false
THREE SAME-LENGTH WORDS, DOTS IN EVERY POSITION INCLUDING A LENGTH MISMATCH
EX 02
WordDictionary()
addWord("a")
search("a") → true
search(".") → true
search("b") → false
MINIMUM-SIZE SINGLE-LETTER WORD
EX 03
WordDictionary()
addWord("cab")
addWord("cabs")
search("ca") → false
search("cab") → true
search("cabs") → true
ONE STORED WORD SITS INSIDE ANOTHER — BOTH MUST BE INDEPENDENTLY FINDABLE
THE HINTS — TAKE ONLY WHAT YOU NEED
HINT 1 THE NUDGE

A dot has to match any single letter — comparing whole strings against every stored word works, but rescans the entire set every time, dot or no dot. What if letters that must match exactly could skip straight past whole branches?

HINT 2 THE STRUCTURE

A trie turns 'skip past what can't match' into 'follow one child pointer.' A literal letter picks exactly one child; a dot is the one spot where you have to try more than one.

HINT 3 ONE STEP FROM THE ANSWER

DFS the trie: on a normal letter, follow the single matching child or fail immediately; on a dot, recurse into every child at that depth and succeed the moment any branch completes the word.

COACH'S BOARD — THE PATTERN, STEP BY STEP
THE WILDCARD DIVEPATTERN · TRIE WITH WILDCARD DFSwords: run, fun, sun · search(".un")
STEP 1

Trie holds run, fun, sun — three added words sharing the suffix 'un'. Query '.un': dot, then literal u, then literal n.

STEP 1 / 7 · ← → WORK TOO
THE SPLITS — TWO PACES, TWO LANGUAGES
grind/design-add-and-search-words-data-structure.pyRACE PACE
LANG ▸
PACE ▸
class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_word = False


class WordDictionary:
    def __init__(self):
        self.root = TrieNode()

    def addWord(self, word: str) -> None:
        node = self.root
        for ch in word:
            node = node.children.setdefault(ch, TrieNode())
        node.is_word = True

    def search(self, word: str) -> bool:
        def dfs(node: TrieNode, i: int) -> bool:
            if i == len(word):
                return node.is_word
            ch = word[i]
            if ch == ".":
                return any(dfs(child, i + 1) for child in node.children.values())
            child = node.children.get(ch)
            return child is not None and dfs(child, i + 1)

        return dfs(self.root, 0)
TIME O(K) NO DOTS, UP TO O(26^D·K) WITH D DOTSSPACE O(Σ CHARS ADDED)PYTHON · RACE PACE · 27 LN

✓ CHIP-TIMED — ALL 4 SOLUTIONS RAN GREEN AGAINST SELF-AUTHORED CASES IN CI · JDK 21 · CPYTHON 3.12 · NOTHING PUBLISHES RED