-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtrie.cpp
More file actions
194 lines (169 loc) · 4.98 KB
/
trie.cpp
File metadata and controls
194 lines (169 loc) · 4.98 KB
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#include "trie.h"
#include <algorithm>
#include <utility>
namespace {
struct MatcherKey {
std::string find_key;
size_t pos;
std::string key;
operator bool() const {
return pos > 0 && key == find_key;
}
bool operator !() const {
return pos == 0;
}
bool Partial() const {
return pos > 0 && key == find_key.substr(0, pos);
}
bool IsPrefix() const {
return pos > 0 && key.substr(0, pos) == find_key;
}
std::string Prefix() const {
return key.substr(0, pos);
}
std::string Sufix() const {
return key.substr(pos);
}
std::string Tail() const {
return find_key.substr(pos);
}
};
/**
* Compares two strings and return position where is different found
* @param lhs the first string
* @param rhs the second string
* @return position where chars are not equal
* if strings are absolutely different, position is 0
* if strings are similar and one of them is just longer,
* position is std::min(lhs.length(), rhs.length())
*
*/
size_t Compare(std::string_view lhs, std::string_view rhs) {
auto min = std::min(lhs.length(), rhs.length());
for (auto i = 0; i < min; ++i) {
if (lhs[i] != rhs[i]) return i;
}
return min;
}
MatcherKey FindSimilarKey(ChildrenPtr children, std::string_view key) {
for (const auto& c: *children) {
auto pos = Compare(c.first, key);
if (pos > 0) {
return {std::string{key}, pos, c.first};
// It is not posible to have few similar keys on the same level
}
}
return {};
}
std::ostream& PrintChildren(std::ostream& out , const Node& node, int deep) {
std::string prefix(deep, '-');
for (const auto& c: *node.children) {
out << prefix << c.first << "\n";
PrintChildren(out, c.second, deep + 1);
}
return out;
}
inline void MarkNode(Node& node, const Data* data) {
node.marker = true;
node.data = data;
}
inline void UnmarkNode(Node& node) {
node.marker = false;
node.data = nullptr;
}
/**
* Compress a child of the node
*/
void CompressNode(Node& parent, const std::string& key) {
auto& node = parent.children->at(key);
if (node.children->size() == 1) {
const auto& child_key = node.children->begin()->first;
const auto& node_child = node.children->begin()->second;
parent.children->emplace(key + child_key, node_child);
parent.children->erase(key);
}
}
Node& AddChild(Node& node, std::string_view key, bool marker, const Data* data,
ChildrenPtr children = std::make_shared<Children>()) {
return node.children->emplace(key, Node{marker, data, children}).first->second;
}
void DeleteNode(Node& parent, const std::string& key) {
auto& node = parent.children->at(key);
if (node.marker) {
if (node.children->empty()) {
parent.children->erase(key);
} else {
UnmarkNode(node);
CompressNode(parent, key);
}
}
}
Result FindElement(const Node& parent, std::string_view key) {
auto matcher = FindSimilarKey(parent.children, key);
if (matcher) {
const auto& node = parent.children->at(matcher.key);
return {node.marker, node.data};
}
if (matcher.Partial()) {
const auto& node = parent.children->at(matcher.key);
return FindElement(node, matcher.Tail());
}
return {false, nullptr};
}
void InsertElement(Node& parent, std::string_view key, const Data* data) {
auto matcher = FindSimilarKey(parent.children, key);
if (!matcher) {
AddChild(parent, key, true, data);
}
else if (matcher) {
auto& node = parent.children->at(matcher.key);
// If key already marked, data will be replace by new one.
MarkNode(node, data);
}
else if (matcher.Partial()) {
auto& node = parent.children->at(matcher.key);
InsertElement(node, matcher.Tail(), data);
}
else if (matcher.IsPrefix()) {
auto& node = AddChild(parent, key, true, data);
const auto& child = parent.children->at(matcher.key);
node.children->emplace(matcher.Sufix(), child);
parent.children->erase(matcher.key);
}
else {
auto& node = AddChild(parent, matcher.Prefix(), false, nullptr);
AddChild(node, matcher.Tail(), true, data);
const auto& child = parent.children->at(matcher.key);
node.children->emplace(matcher.Sufix(), child);
parent.children->erase(matcher.key);
}
}
void RemoveElement(Node& parent, std::string_view key) {
auto matcher = FindSimilarKey(parent.children, key);
if (matcher) {
DeleteNode(parent, matcher.key);
}
else if (matcher.Partial()) {
auto& node = parent.children->at(matcher.key);
RemoveElement(node, matcher.Tail());
if (!node.marker) {
CompressNode(parent, matcher.key);
}
}
}
} // namespace
Node& Node::operator[](const std::string& key) {
return (*children)[key];
}
void Trie::Insert(std::string_view key, const Data* data) {
InsertElement(*this, key, data);
}
Result Trie::Find(std::string_view key) const {
return FindElement(*this, key);
}
void Trie::Remove(std::string_view key) {
RemoveElement(*this, key);
}
std::ostream& operator<<(std::ostream& out, const Trie& tree) {
return PrintChildren(out, reinterpret_cast<const Node&>(tree), 0);
}