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.Example:
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] Explanation 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.2
dots in word
for search
queries.104
calls will be made to addWord
and search
.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to this word search problem involves simply storing every word we are given, and then, when asked to search, checking all stored words to see if they match the pattern. A match with a wildcard means checking all possible letters that could replace the wildcard.
Here's how the algorithm would work step-by-step:
class WordDictionary:
def __init__(self):
self.word_list = []
def addWord(self, word: str) -> None:
self.word_list.append(word)
def search(self, word: str) -> bool:
# Iterate through each word we've stored
for stored_word in self.word_list:
if len(stored_word) != len(word):
continue
# Compare the characters
match = True
for index in range(len(word)):
if word[index] == '.':
continue
if word[index] != stored_word[index]:
match = False
break
# Found a match, return True
if match:
return True
# If we made it here, no matches found
return False
We'll create a structure to store words, making it efficient to add new words and search for words, even with wildcard characters. We'll use a special kind of tree where each level represents a character in a word, which will make our search super fast.
Here's how the algorithm would work step-by-step:
class WordDictionary:
def __init__(self):
self.trie_root = {}
def addWord(self, word: str) -> None:
current_node = self.trie_root
for char in word:
if char not in current_node:
current_node[char] = {}
current_node = current_node[char]
current_node['$'] = True
def search(self, word: str) -> bool:
def search_in_node(word, node):
for i, char in enumerate(word):
if char == '.':
# Explore all branches for wildcard.
for child in node:
if child != '$' and search_in_node(word[i+1:], node[child]):
return True
return False
else:
if char not in node:
return False
node = node[char]
# Check if the path leads to a complete word.
return '$' in node
# Initiate search from the root of the trie.
return search_in_node(word, self.trie_root)
Case | How to Handle |
---|---|
Null or empty word input for addWord | Ignore null/empty words or throw an IllegalArgumentException based on requirements; the Trie's structure is not altered. |
Null or empty word input for search | Return true if the Trie is empty (no words added) or false if it's not, based on specific project requirement. |
Word containing only '.' for search, and empty Trie | Return false because an empty Trie cannot match even a wildcard search. |
Word containing only '.' for search, and Trie containing words of a different length | Return false if there are no words of the same length as the search word. |
Very long words added to the dictionary, causing deep Trie branches | Trie structure can handle very long words, but deep recursion in search may lead to stack overflow, requiring iterative DFS. |
Adding the same word multiple times | Adding the same word multiple times simply adds another path leading to 'isWord', which does not change search results. |
Words with unicode characters | Trie nodes should be able to store unicode characters or be explicitly limited to ASCII. |
Maximum number of addWord/search calls leading to excessive memory usage | Consider memory usage and potentially implement a Least Recently Used (LRU) cache or some other mechanism to limit Trie size if needed. |