Design Add and Search Words Data Structure
1. Clarify the problem: Before diving into solving the problem, let's clarify the requirements:
- We need to design a data structure to store words and perform two operations:
addWord
andsearch
. - The
addWord
operation adds a word to the data structure. - The
search
operation searches for a word in the data structure. The word can contain letters or a dot character '.' that can match any letter. - The
search
operation should return true if the word exists in the data structure, and false otherwise.
2. Analyze the problem: To solve this problem, we can use a trie data structure, which is a tree-like data structure commonly used for efficient string matching and retrieval.
3. Design an algorithm: Here is the algorithm to design the data structure and implement the operations:
- Define a class called
WordDictionary
to represent the data structure. - Define a class called
TrieNode
to represent a node in the trie data structure. Each node will have a character value, a flag indicating whether it represents the end of a word, and a dictionary to store its child nodes. - In the
WordDictionary
class, implement theaddWord
operation:- Start from the root of the trie.
- For each character in the word, check if there is a child node corresponding to that character. If not, create a new node and add it as a child.
- Move to the child node and repeat the process for the next character.
- After processing all characters, mark the last node as the end of a word.
- In the
WordDictionary
class, implement thesearch
operation:- Start from the root of the trie.
- For each character in the word:
- If the character is a dot ('.'), recursively search in all child nodes of the current node.
- If there is a child node corresponding to the character, move to that node and continue.
- If there is no child node corresponding to the character or dot, return false.
- After processing all characters, check if the last node represents the end of a word. If so, return true; otherwise, return false.
4. Explain your approach:
The approach involves designing a trie data structure to store the words. Each node in the trie represents a character, and the edges between nodes represent the transitions between characters. We use a flag in each node to indicate whether it represents the end of a word. For the search
operation, we traverse the trie based on the characters in the search word. If we encounter a dot ('.'), we recursively search in all child nodes. If we find a character that does not match or a missing child node, we return false. If we reach the end of the search word and the last node represents the end of a word, we return true.
5. Write clean and readable code:
python
class TrieNode:
def __init__(self):
self.children = {}
self.is_word_end = False
class WordDictionary:
def __init__(self):
self.root = TrieNode()
def addWord(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_word_end = True
def search(self, word):
return self.searchRecursive(self.root, word)
def searchRecursive(self, node, word):
if not word:
return node.is_word_end
char = word[0]
if char != '.':
if char not in node.children:
return False
return self.searchRecursive(node.children[char], word[1:])
else:
for child in node.children.values():
if self.searchRecursive(child, word[1:]):
return True
return False
6. Test your code:
python
# Create a WordDictionary object
wordDictionary = WordDictionary()
# Test the addWord operation
wordDictionary.addWord("bad")
wordDictionary.addWord("dad")
wordDictionary.addWord("mad")
# Test the search operation
print(wordDictionary.search("pad")) # Expected output: False
print(wordDictionary.search("bad")) # Expected output: True
print(wordDictionary.search(".ad")) # Expected output: True
print(wordDictionary.search("b..")) # Expected output: True
# Add more test cases to validate the solution
7. Optimize if necessary: The provided solution is already efficient with a time complexity of O(M), where M is the length of the word being searched. The space complexity is O(N), where N is the total number of characters in all added words.
8. Handle error cases: The code assumes that the input words are valid strings containing only lowercase letters. If the input is empty or contains invalid characters, the behavior of the code is undefined.
9. Discuss complexity analysis:
The time complexity of the addWord
operation is O(L), where L is the length of the word being added. The time complexity of the search
operation is O(M), where M is the length of the word being searched. The space complexity is O(N), where N is the total number of characters in all added words. The solution has an optimal time and space complexity given the problem requirements.