Skip to content
AI360Xpert
Back to Tries
Medium

Design Add and Search Words Data Structure

Design a data structure that supports adding new words and finding if a string matches any previously added string. Implement the `WordDictionary` class: `WordDictionary()` Initializes the object. `void addWord(word)` Adds `word` to the data structure, it can be matched later. `bool search(word)` Returns `true` if there is any string in the data structure that matches `word` or `false` otherwise. `word` may contain dots `'.'` where dots can be matched with any letter.

Examples

Input:["WordDictionary","addWord","addWord","addWord","search","search","search","search"] [[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
Output:[null,null,null,null,false,true,true,true]
WordDictionary wordDictionary = new WordDictionary(); wordDictionary.addWord("bad"); wordDictionary.addWord("dad"); wordDictionary.addWord("mad"); wordDictionary.search("pad"); // return False wordDictionary.search("bad"); // return True wordDictionary.search(".ad"); // return True wordDictionary.search("b.."); // return True

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.

Trie with DFS for Dot Wildcards

Approach

Adding words is identical to standard Trie insertion. For searching, if we encounter a regular character, we traverse the child node as usual. If we encounter a '.' wildcard, we must explore ALL possible child nodes. We can use a recursive Depth-First Search (DFS) helper function. If `dfs` returns true for any child path, we return true. If we reach the end of the word, we check `isEndOfWord`.

Complexity Analysis

Time Complexity
O(m) add, O(26^m) worst-case search
Space Complexity
O(m * N)

Due to the wildcard '.', search can potentially branch to all 26 children at each step, though in practice it is constrained by existing paths.

Solution.java
class TrieNode {    TrieNode[] children;    boolean isEndOfWord;        public TrieNode() {        children = new TrieNode[26];        isEndOfWord = false;    }}
class WordDictionary {    private TrieNode root;
    public WordDictionary() {        root = new TrieNode();    }        public void addWord(String word) {        TrieNode curr = root;        for (char c : word.toCharArray()) {            int index = c - 'a';            if (curr.children[index] == null) {                curr.children[index] = new TrieNode();            }            curr = curr.children[index];        }        curr.isEndOfWord = true;    }        public boolean search(String word) {        return dfs(word, 0, root);    }        private boolean dfs(String word, int index, TrieNode node) {        if (index == word.length()) {            return node.isEndOfWord;        }                char c = word.charAt(index);                if (c == '.') {            // Explore all possible children            for (int i = 0; i < 26; i++) {                if (node.children[i] != null && dfs(word, index + 1, node.children[i])) {                    return true;                }            }            return false;        } else {            // Standard search            int childIndex = c - 'a';            if (node.children[childIndex] == null) {                return false;            }            return dfs(word, index + 1, node.children[childIndex]);        }    }}