-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
/
trie_modern.cpp
168 lines (149 loc) · 5.53 KB
/
trie_modern.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* @file
*
* @author Anmol3299
* \brief A basic implementation of trie class to store only lower-case strings.
*/
#include <iostream> // for io operations
#include <memory> // for std::shared_ptr<>
#include <string> // for std::string class
/**
* A basic implementation of trie class to store only lower-case strings.
* You can extend the implementation to all the ASCII characters by changing the
* value of @ ALPHABETS to 128.
*/
class Trie {
private:
static constexpr size_t ALPHABETS = 26;
/**
* Structure of trie node.
* This struct doesn't need a constructor as we are initializing using
* intializer list which is more efficient than if we had done so with
* constructor.
*/
struct TrieNode {
// An array of pointers of size 26 which tells if a character of word is
// present or not.
std::shared_ptr<TrieNode> character[ALPHABETS]{nullptr};
bool isEndOfWord{false};
};
/**
* Function to check if a node has some children which can form words.
* @param node whose character array of pointers need to be checked for
* children.
* @return `true` if a child is found
* @return `false` if a child is not found
*/
inline static bool hasChildren(std::shared_ptr<TrieNode> node) {
for (size_t i = 0; i < ALPHABETS; i++) {
if (node->character[i]) {
return true;
}
}
return false;
}
/**
* A recursive helper function to remove a word from the trie. First, it
* recursively traverses to the location of last character of word in the
* trie. However, if the word is not found, the function returns a runtime
* error. Upon successfully reaching the last character of word in trie, if
* sets the isEndOfWord to false and deletes the node if and only if it has
* no children, else it returns the current node.
* @param word is the string which needs to be removed from trie.
* @param curr is the current node we are at.
* @param index is the index of the @word we are at.
* @return if current node has childern, it returns @ curr, else it returns
* nullptr.
* @throw a runtime error in case @ word is not found in the trie.
*/
std::shared_ptr<TrieNode> removeWordHelper(const std::string& word,
std::shared_ptr<TrieNode> curr,
size_t index) {
if (word.size() == index) {
if (curr->isEndOfWord) {
curr->isEndOfWord = false;
}
if (hasChildren(curr)) {
return curr;
}
return nullptr;
}
size_t idx = word[index] - 'a';
// Throw a runtime error in case the user enters a word which is not
// present in the trie.
if (!curr->character[idx]) {
throw std::runtime_error(std::move(std::string("Word not found.")));
}
curr->character[idx] =
removeWordHelper(word, curr->character[idx], index + 1);
// This if condition checks if the node has some childern.
// The 1st if check, i.e. (curr->character[idx]) is checked specifically
// because if the older string is a prefix of some other string, then,
// there would be no need to check all 26 characters. Example- str1 =
// abbey, str2 = abbex and we want to delete string "abbey", then in
// this case, there would be no need to check all characters for the
// chars a,b,b.
if (curr->character[idx] || hasChildren(curr)) {
return curr;
}
return nullptr;
}
public:
/// constructor to initialise the root of the trie.
Trie() : m_root(std::make_shared<TrieNode>()) {}
/**
* Insert a word into the trie.
* @param word which needs to be inserted into the string.
*/
void insert(const std::string& word) {
auto curr = m_root;
for (char ch : word) {
size_t index = ch - 'a';
// if a node for current word is not already present in trie, create
// a new node for it.
if (!curr->character[index]) {
curr->character[index] = std::make_shared<TrieNode>();
}
curr = curr->character[index];
}
curr->isEndOfWord = true;
}
/**
* Search if a word is present in trie or not.
* @param word which is needed to be searched in the trie.
* @return True if the word is found in trie and isEndOfWord is set to true.
* @return False if word is not found in trie or isEndOfWord is set to
* false.
*/
bool search(const std::string& word) {
auto curr = m_root;
for (char ch : word) {
size_t index = ch - 'a';
// if any node for a character is not found, then return that the
// word cannot be formed.
if (!curr->character[index]) {
return false;
}
curr = curr->character[index];
}
return curr->isEndOfWord;
}
// Function to remove the word which calls the helper function.
void removeWord(const std::string& word) {
m_root = removeWordHelper(word, m_root, 0);
}
private:
// data member to store the root of the trie.
std::shared_ptr<TrieNode> m_root;
};
/**
* Main function
*/
int main() {
Trie trie;
trie.insert("hel");
trie.insert("hello");
trie.removeWord("hel");
std::cout << trie.search("hello") << '\n';
return 0;
}