This problem is an application of the Trie data structure. In the following, it is assumed that you have solved Implement Trie (Prefix Tree).
Now, let's first look at the TrieNode
class. I define it as follows.
1 class TrieNode { 2 public: 3 bool isKey; 4 TrieNode* children[26]; 5 TrieNode() { 6 isKey = false; 7 for (int i = 0; i < 26; i++) 8 children[i] = NULL; 9 } 10 };
The field isKey
is to label whether the string comprised of characters starting from the root to the current node is a key (word that was added). In this problem, only lower-case letters a - z
need to be considered, so each TrieNode
have at most 26
children. I store it in an array of TrieNode*
: children[i]
corresponds to letter 'a' + i
. The remaining code defines the initialization of the TrieNode
class.
Adding a word can be done in the same way as in Implement Trie (Prefix Tree). The basic idea is that we create a node correspond to each letter in the word. When we are done, label the last node to be a key (set isKey = true
). The code is as follows.
1 void addWord(string word) { 2 TrieNode* node = root; 3 for (int i = 0; i < word.length(); i++) { 4 if (!(node -> children[word[i] - 'a'])) 5 node -> children[word[i] - 'a'] = new TrieNode(); 6 node = node -> children[word[i] - 'a']; 7 } 8 node -> isKey = true; 9 }
By the way, root
is defined as private data of WordDictionary
:
1 private: 2 TrieNode* root;
And the WordDictionary
class has a constructor to initialize root
:
1 WordDictionary() { 2 root = new TrieNode(); 3 }
Now we are left only with search
. Let's do it. The basic idea is still the same as typical search operations in a Trie. The critical part is how to deal with the dots .
. Well, my solution is very naive in this place. Each time when we reach a .
, just traverse all the children of the current node and recursively search the remaining substring in word
from the children. So I define a helper function searchHelper
for search
that takes in a string and a starting node. And the initial call to searchHelper
is like searchHelper(word, root)
.
By the way, I pass a char*
instead of string
to searchHelper
and it greatly speeds up the code. So the initial call to searchHelper
is actually searchHelper(word.c_str(), root)
.
Now I put all the codes together below. Hope it to be useful!
1 class TrieNode { 2 public: 3 bool isKey; 4 TrieNode* children[26]; 5 TrieNode() { 6 isKey = false; 7 for (int i = 0; i < 26; i++) 8 children[i] = NULL; 9 } 10 }; 11 12 class WordDictionary { 13 public: 14 WordDictionary() { 15 root = new TrieNode(); 16 } 17 18 // Adds a word into the data structure. 19 void addWord(string word) { 20 TrieNode* run = root; 21 for (int i = 0; i < word.length(); i++) { 22 if (!(run -> children[word[i] - 'a'])) 23 run -> children[word[i] - 'a'] = new TrieNode(); 24 run = run -> children[word[i] - 'a']; 25 } 26 run -> isKey = true; 27 } 28 29 // Returns if the word is in the data structure. A word could 30 // contain the dot character '.' to represent any one letter. 31 bool search(string word) { 32 return searchHelper(word.c_str(), root); 33 } 34 35 private: 36 TrieNode* root; 37 38 bool searchHelper(const char* word, TrieNode* node) { 39 TrieNode* run = node; 40 for (int i = 0; word[i]; i++) { 41 if (run && word[i] != '.') 42 run = run -> children[word[i] - 'a']; 43 else if (run && word[i] == '.') { 44 TrieNode* tmp = run; 45 for (int j = 0; j < 26; j++) { 46 run = tmp -> children[j]; 47 if (searchHelper(word + i + 1, run)) 48 return true; 49 } 50 } 51 else break; 52 } 53 return run && run -> isKey; 54 } 55 }; 56 57 // Your WordDictionary object will be instantiated and called as such: 58 // WordDictionary wordDictionary; 59 // wordDictionary.addWord("word"); 60 // wordDictionary.search("pattern");