This is a simple Trie data structure for strings, except it puts the strings into the structure backwards.
The insert method simply iterates over chars from the string-to-be-inserted backwards, and starting at the root of the graph, looks for an edge that is labeled with the current char. If an edge is found, we move to the next vertex and next char. If no edge is found, a new edge and target vertex is created, with the new edge labeled with the current char.
The containsWord
method is similar. It follows labeled edges until it comes to the a word terminator '\0'
or returns false as soon as it cannot find the next edge.
I'm fairly new to C++. I'd like to know about any style errors or anything outside of accepted convention. Or if this is just not a good way of creating this structure.
My unit tests are working ok with this, but performance seems to be lagging a boost::unordered_set
for lookups. Tries are supposed to have \$O(length of key)\$ lookup time, which I would think would be about the same as the cost of calculating the hash code of a string to look up in a set.
#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
class TrieVertex;
typedef boost::shared_ptr<TrieVertex> VertexPtr;
class TrieEdge
{
public:
char fC;
VertexPtr fTarget;
TrieEdge(char c, VertexPtr target) : fC(c), fTarget(target) {}
};
typedef boost::shared_ptr<TrieEdge> EdgePtr;
class TrieVertex {
public:
boost::unordered_map<char, EdgePtr> fOutEdges;
TrieVertex() : fOutEdges() {}
};
class ReverseTrie {
private:
VertexPtr fRoot;
size_t fSize;
public:
ReverseTrie( ): fRoot(boost::make_shared<TrieVertex>()), fSize(0) {}
~ReverseTrie() {
fRoot.reset();
}
void clear() {
boost::make_shared<TrieVertex>().swap(fRoot);
fSize = 0;
}
size_t size() {
return fSize;
}
bool insertStringR(const std::string &word) {
VertexPtr currentVertex = fRoot;
for (std::string::const_reverse_iterator i = word.rbegin(); i != word.rend(); ++i) {
if (*i == '\0') {
continue;
}
if (currentVertex->fOutEdges.size() == 0 || currentVertex->fOutEdges.count(*i) == 0) {
VertexPtr targetVertex = boost::make_shared<TrieVertex>();
EdgePtr e = boost::make_shared<TrieEdge>(*i, targetVertex);
currentVertex->fOutEdges[*i] = e;
currentVertex = e->fTarget;//*currentVertex.fOutEdges[*i]->fTarget;
} else {
EdgePtr e = currentVertex->fOutEdges[*i];
currentVertex = e->fTarget;
}
}
bool exists = true;
//end of word.
if (currentVertex->fOutEdges.count('\0') == 0) {
VertexPtr targetVertex = boost::make_shared<TrieVertex>();
EdgePtr e = boost::make_shared<TrieEdge>('\0', targetVertex);
currentVertex->fOutEdges['\0'] = e;
exists = false;
}
fSize++;
return exists;
}
bool containsString(const std::string &word) {
VertexPtr currentVertex = fRoot;
for (std::string::const_reverse_iterator i = word.rbegin(); i != word.rend(); ++i) {
if (*i == '\0') {
continue;
}
if (currentVertex->fOutEdges.size() == 0 || currentVertex->fOutEdges.count(*i) == 0) {
return false;
} else {
currentVertex = currentVertex->fOutEdges[*i]->fTarget;
}
}
if (currentVertex->fOutEdges.count('\0') == 0) {
return false;
}
return true;
}
void logEdges(VertexPtr vp) {
for ( boost::unordered_map<char, EdgePtr>::iterator it = vp->fOutEdges.begin(); it != vp->fOutEdges.end(); it++) {
char c = it->first;
EdgePtr ep = it->second;
std::string output;
if (c == '\0')
output.append("EOW");
else
output.push_back(c);
logWarning("---", "edge: %s", output.c_str());
logEdges(ep->fTarget);
}
}
void logEdges() {
logEdges(fRoot);
}
};