--- title: '211. Design Add and Search Words Data Structure' description: Design a data structure that supports adding new words and finding if a string matches any previously added string sidebar: label: 'Design Add and Search Words Data Structure' badge: 'Medium' --- Trie ### Constraints: - `1 <= word.length <= 25` - `word` in addWord consists of lowercase English letters. - `word` in search consist of '.' or lowercase English letters. - There will be at most 2 dots in `word` for search queries. - At most 10^4 calls will be made to addWord and search. ## Approach ```mermaid flowchart TD subgraph state["state — root TrieNode; every node holds a children dict and an endOfWord flag"] R["root"] --- C["... children[c] ..."] --- L["endOfWord = True — a word ends here"] end subgraph addWord["addWord(word)"] A1["cur = root"] --> A2{"more c in word?"} A2 -- yes --> A3{"c not in cur.children?"} A3 -- yes --> A4["cur.children[c] = TrieNode()"] A4 --> A5["cur = cur.children[c]"] A3 -- no --> A5 A5 --> A2 A2 -- no --> A6["cur.endOfWord = True"] A6 --> A7(["done"]) end subgraph search["search(word)"] S1["dfs(0, root) — match from index 0 at the root"] --> S2["cur = root"] S2 --> S3{"more i in range(j, len(word))?"} S3 -- no --> S4(["return cur.endOfWord"]) S3 -- yes --> S5["c = word[i]"] S5 --> S6{"c == '.'?"} S6 -- yes --> S7{"more child in cur.children.values()?"} S7 -- yes --> S8{"dfs(i + 1, child)? — try the rest of the word from this child"} S8 -- yes --> S9(["return True"]) S8 -- no --> S7 S7 -- no --> S10(["return False — no child matched the wildcard"]) S6 -- no --> S11{"c not in cur.children?"} S11 -- yes --> S12(["return False"]) S11 -- no --> S13["cur = cur.children[c]"] S13 --> S3 end state --> addWord state --> search ``` ## Solution ```py class TrieNode: def __init__(self): self.children = {} self.endOfWord = False class WordDictionary: def __init__(self): self.root = TrieNode() def addWord(self, word: str) -> None: cur = self.root for c in word: if c not in cur.children: cur.children[c] = TrieNode() cur = cur.children[c] cur.endOfWord = True # Leverage Recursion def search(self, word: str) -> bool: def dfs(j, root): cur = root for i in range(j, len(word)): c = word[i] if c == ".": for child in cur.children.values(): if dfs(i + 1, child): return True return False else: if c not in cur.children: return False cur = cur.children[c] return cur.endOfWord return dfs(0, self.root) # Your WordDictionary object will be instantiated and called as such: # obj = WordDictionary() # obj.addWord(word) # param_2 = obj.search(word) ``` ## Explanation