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.For example:
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
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:
We're building a system to store words and search for them later. A brute force approach for searching simply checks every word we have stored until we find one that matches, trying all possibilities. This is like searching for a book in a library by looking at every book on every shelf.
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 have stored
for existing_word in self.word_list:
if len(existing_word) != len(word):
continue
match = True
# Check each letter in the current stored word
for index in range(len(word)):
if word[index] == '.':
continue
# If chars do not match, not a valid word.
if word[index] != existing_word[index]:
match = False
break
# Return true if the current word matches
if match:
return True
return False
We will create a data structure that efficiently stores words and allows us to search for words, even with wildcard characters. The key is to use a structure that lets us quickly narrow down the search instead of checking every word individually.
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 a 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 end of word is marked at the end of search
return '#' in node
return search_in_node(word, self.trie_root)
Case | How to Handle |
---|---|
Null or empty word being added | Treat null or empty words as invalid inputs and either ignore them or throw an exception. |
Null or empty search pattern | An empty search pattern should likely return true if and only if an empty string was added before; otherwise, it is handled like any other pattern. |
Very long words or a very large number of words added | The Trie data structure's memory usage and search time could become a bottleneck; ensure efficient memory allocation and consider alternative data structures if scalability is a major concern. |
Search pattern with a large number of '.' characters | Excessive wildcard characters could lead to exponential backtracking, potentially causing timeouts, so limit the depth of recursion or use dynamic programming. |
Words containing characters other than lowercase letters 'a'-'z' | Handle non-lowercase characters by either rejecting them or extending the Trie to support them, based on problem requirements. |
Adding the same word multiple times | Adding duplicates should not affect the search functionality; Trie structure inherently handles multiple insertions of same word correctly. |
No words have been added before searching | Searching should return false for any non-empty pattern if no words have been added to the data structure. |
Integer overflow when calculating indices in large Tries | Ensure that index calculations within the Trie remain within integer bounds to avoid unexpected behavior. |