1. Trie
A Trie is a tree-like data structure used to store a dynamic set of strings, where each node represents a character of a string.
Each node has:
- An array/hashmap of child nodes (e.g., 26 for lowercase a–z)
- A boolean flag isEnd to mark the end of a word
What can Trie help us:
- Hold a list of strings
- search a string is in the list. Time Complexity: O(n) where n is the lenght of the longest string
- Get the common prefix for strings we see so far
2. code
The following code assumes, the string only contains lowercase letters. And is using a vector of length 26 to hold lower case letters in each level. For other cases, just change the vector to unorderedmap<char, TrieNode*>
#define NUMBER_OF_LOWERCASE_LETTERS 26 class Trie { class TrieNode { vector<TrieNode*> children; bool is_end; TrieNode() : children(NUMBER_OF_LOWERCASE_LETTERS, nullptr), is_end(false) {} }; TrieNode* root; public: Trie() { root = new TrieNode; } void insert(string& s) { TrieNode* cur = root; for(char ch : s) { if(!cur->children[ch-'a']) { cur->children[ch] = new TrieNode(); } cur = cur->children[ch]; } // now cur is pointing to the end char node and end of string cur->is_end = true; } // return true if string s is in the Trie bool search(string& s) { TrieNode* cur = root; for(char ch : s) { // go to char node cur = cur->children[ch-'a']; if(cur == nullptr) return false; } // need to check if there is string ends here return cur->is_end; } // command prefix: break at the branching point (node that have two or more childrent) string common_prefix() { string prefix = ""; TrieNode* cur = root; while(cur != nullptr) { // should have only one child int child = -1, count = 0; vector<TrieNode*> children = cur->children; for(int i=0; i<children.size(); i++) { if(children[i]) { child = i; count ++; } if(count > 1) break; } if(count > 1) { break; } // now count = 1 and child is the index of child prefix += ('a' + child); cur = cur->children[child]; } return prefix; } void free_trie(TrieNode* cur) { if(cur == nullptr) return; // 1. free children for(TrieNode* child : cur->children) { free_trie(child); } //2. free itself delete cur; } ~Trie() { free_trie(root); } };