A trie (pronounced as “try”) or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker.
Implement the Trie class:
Trie() Initializes the trie object. void insert(String word) Inserts the string word into the trie. boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.
1 <= word.length, prefix.length <= 2000 word and prefix consist only of lowercase English letters. At most 3 * 104 calls in total will be made to insert, search, and startsWith.
My solution
My solution is easy to understand but cost a lot of time to process.
classTrie { public: Trie() { } voidinsert(string word){ wordSet.insert(word); } boolsearch(string word){ if (wordSet.find(word) != wordSet.end()) returntrue; elsereturnfalse; } boolstartsWith(string prefix){ int n = prefix.size(); for(string word: wordSet){ for(int i = 0; i < n; i++){ if(prefix[i] != word[i]) break; if(prefix[i] == word[i] && i == n - 1) returntrue; } } returnfalse; }
private: unordered_set <string> wordSet;
};
/** * Your Trie object will be instantiated and called as such: * Trie* obj = new Trie(); * obj->insert(word); * bool param_2 = obj->search(word); * bool param_3 = obj->startsWith(prefix); */
Prefix Tree
I got the idea of using prefix tree from this post in Leetcode.
classTrie { public: TrieNode *root; Trie() { root = newTrieNode(); } voidinsert(string word){ TrieNode* newRoot = root; for (char ch : word) { // get the index of the alphabet int alphabetIndex = ch - 'a'; // if the pointer is NULL, create a new TrieNode if (newRoot -> children[alphabetIndex] == NULL) { newRoot -> children[alphabetIndex] = newTrieNode(); } // move the pointer to the next TrieNode newRoot = newRoot -> children[alphabetIndex]; } // mark the word as completed newRoot -> isWordCompleted = true; } boolsearch(string word){ TrieNode* newRoot = root; for (char ch : word) { int alphabetIndex = ch - 'a'; // if the pointer is NULL, means the character is not in the tree if (newRoot -> children[alphabetIndex] == NULL) { returnfalse; } newRoot = newRoot -> children[alphabetIndex]; } // if the word is completed, return true if (newRoot -> isWordCompleted == true) { returntrue; } returnfalse; } boolstartsWith(string prefix){ TrieNode* newRoot = root; for (char ch : prefix) { int alphabetIndex = ch - 'a'; if (newRoot -> children[alphabetIndex] == NULL) { returnfalse; } newRoot = newRoot -> children[alphabetIndex]; } returntrue; } };
/** * Your Trie object will be instantiated and called as such: * Trie* obj = new Trie(); * obj->insert(word); * bool param_2 = obj->search(word); * bool param_3 = obj->startsWith(prefix); */
Design Add and Search Words Data Structure
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.
1 <= word.length <= 25 word in addWord consists of lowercase English letters. word in search consist of '.' or lowercase English letters. There will be at most 2 dots in word for search queries. At most 104 calls will be made to addWord and search.
Solution
Based on the idea of previous problem, we can use prefix tree to solve this problem. The only difference is that we need to consider the ‘.’ in the word. We can use recursive method to search the word in the tree. If we meet ‘.’, we need to search all the children of the current node. If the node we find is not correct, we need to move to the next node. If we run out of all 26 alphabet, we return false.
classWordDictionary { public: TrieNode *root ; WordDictionary() { root = newTrieNode(); } voidaddWord(string word){ TrieNode * newroot = root; for(char character: word){ int alphabetIndex = character - 'a'; if(newroot->children[alphabetIndex] == NULL) newroot->children[alphabetIndex] = newTrieNode(); newroot = newroot->children[alphabetIndex]; } newroot -> isCompleted = true; } boolsearch(string word){ returnsearchRecursive(word, 0, root); } private: boolsearchRecursive(const string & word, int index, TrieNode* node){ if (index == word.size()){ return node->isCompleted; } char ch = word[index]; if(ch != '.'){ int alphabetIndex = ch - 'a'; if(node->children[alphabetIndex] == NULL) returnfalse; returnsearchRecursive(word, index + 1, node->children[alphabetIndex]); } if(ch == '.'){ for (int i = 0; i < 26; i++){ if(node->children[i] != NULL){ if (searchRecursive(word, index + 1, node->children[i])) returntrue; } } } returnfalse; } };
/** * Your WordDictionary object will be instantiated and called as such: * WordDictionary* obj = new WordDictionary(); * obj->addWord(word); * bool param_2 = obj->search(word); */