Skip to content
AI360Xpert
Beta
Difficulty: MediumTries

Design Add and Search Words Data Structure

Problem in Plain English

Design a word dictionary that stores every added word and reports whether a query matches any stored word, where a dot in the query stands for any single letter. Adding is exact, while searching must handle wildcards.

Problem Statement

Design a data structure that supports adding new words and finding if a string matches any previously added string.

Implement the WordDictionary class with void addWord(word), which stores word for later matching, and bool search(word), which returns true if some stored word matches the query. The query may contain dots ., where each dot matches any single lowercase English letter.

Constraints

  • 1 <= word.length <= 25
  • word in addWord consists of lowercase English letters.
  • word in search consists 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.

Examples

Example 1

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]

After storing bad, dad and mad, pad matches nothing, bad matches exactly, .ad matches all three, and b.. matches bad.

Example 2

Input
["WordDictionary","search","addWord","addWord","search","search"] [[],["."],["a"],["a"],["."],["aa"]]
Output
[null,false,null,null,true,false]

The empty dictionary matches nothing. Adding a twice does not change existence semantics: . matches the one-letter word, while aa needs two letters.

Example 3

Input
["WordDictionary","addWord","addWord","search","search","search","search","search"] [[],["ab"],["acd"],[".."],["a."],[".c"],["ac"],["ac."]]
Output
[null,null,null,true,true,false,false,true]

.. and a. match ab. Neither .c nor ac is a complete added word: ac is only a prefix of acd. In ac., the dot consumes the final d, so it matches acd.

Intuition

A dot consumes exactly one character, so a query can only match a complete stored word of the same length. In Example 1, .ad can use the first letter of bad, dad, or mad. Length buckets compare whole candidates; a trie shares prefixes and explores alternative children only at dots. Reaching a prefix is insufficient unless that node marks the end of an added word.

Approaches

Group by Length

Solution Details

Reveal Group by Length: intuition, complexity, and code

Hints

Hint 1
A dot matches one letter, so first discard stored words whose length differs from the query.
Hint 2
Shared prefixes can be stored once in a trie; an exact letter follows one child.
Hint 3
At a dot, try every existing child and succeed if any continuation succeeds. Accept at the end only when the node is marked as a word end.

Edge Cases

  • Search before any insertion returns false, including a dot query.
  • Duplicate additions preserve existence; a list bucket can retain repeated references while a trie reuses the same path.
  • An inserted prefix such as a and a longer word such as ab each need their own terminal marker.
  • A prefix such as ac of acd is not automatically a word.
  • A query with two dots can branch at both positions, but every branch must consume exactly the query length.
  • Length 25 and 10000 total calls are legal; at most two query positions are dots.

Common Mistakes and Interview Tips

  • Accepting a trie node when the query ends without checking its word-end flag accepts unadded prefixes.
  • Treating a dot like a star that consumes zero or many letters changes the matching rule.
  • Returning false after the first failed wildcard branch instead of trying its siblings loses valid matches.
  • Failing to advance the query index for a wildcard consumes the wrong number of letters.
  • Claiming trie searches always take linear time ignores wildcard branching; the bound depends on the number of dots.

Key Takeaway

When prefix storage meets wildcard queries, keep the words in a trie and let depth-first search fan out only where the query actually branches.