我有一个前缀树来存储大量的单词。现在,如果我想找到所有带有公共前缀的单词“a”,我首先检索包含a的第一个节点,然后在第一个节点的子节点中以Depth First方式详尽地搜索。虽然这个想法看起来天真和直截了当,但如果具有共同前缀的可能数量的单词非常高(> 20K),事实上它实际上是可怜的。有没有其他方法可以有效地检索所有以公共前缀开头的单词?或者我应该采用其他一些数据结构?高级感谢你。
EDIT1 基本上我通过访问每个节点并逐步添加字符来创建一个完整的单词。所有单词后来都存储在矢量容器中。是的,我有递归实现。
EDIT2
vector<int> getNonEmptyEdgeIndices(Node* parent) {
vector<int> indices;
for(int i=0; i<EDGE; i++) {
if (parent->edges[i] != NULL) {
indices.push_back(i);
}
}
return indices;
}
vector<string> getSubsequentStrings(vector<string> wordsStartingWith, Node* node, string prefix) {
vector<int> indices = getNonEmptyEdgeIndices(node);
// push the word to the container if node is a leaf
if (indices.empty()) {
wordsStartingWith.push_back(prefix);
return wordsStartingWith;
}
// if frequency is set in node, push the word but still continue recursion
if (node->frequency != 0) {
wordsStartingWith.push_back(prefix);
}
// look all the children of the node
for(unsigned int i=0; i<indices.size(); i++) {
string newPrefix = prefix + getNodeChar(indices[i]);
Node* child = node->edges[indices[i]];
// recursively get the prefix for all children
wordsStartingWith = getSubsequentStrings(wordsStartingWith, child, newPrefix);
}
return wordsStartingWith;
}
vector<string> Trie::getWordsStartingWith(string prefix) {
vector<string> wordsStartingWith;
Node* lastNode = getLastNode(prefix);
if (lastNode != NULL) {
wordsStartingWith = getSubsequentStrings(wordsStartingWith, lastNode, prefix);
}
return wordsStartingWith;
}
编辑3 解决了!!!我的实施实际上存在问题。我在递归调用中传递了这个巨大的向量字符串容器,这实际上是问题所在。谢谢大家的好意见。
答案 0 :(得分:0)
实际上 TRIE + DFT已经是一个足够好的解决方案。其时间复杂度为O(M+B^M)
,其中M
是单词的最大长度,B
是可能的字母数(通常为B=26
)。虽然它是指数级的,但在实践中它可能比你想象的要快得多,因为TRIE树非常稀疏而M
是一个很小的数字。
更简单(不能保证更好)的解决方案是将所有单词排序为数组。然后,您可以通过二进制搜索数组获得您想要的内容,以获得具有目标前缀的第一个和最后一个单词,就像您使用英语词典一样。排序需要O(NlogN)
,搜索需要O(MlogN)
,其中N
是单词的数量。这是多项式。
如果你真的极品飞车,那么你几乎总能为交换支付内存空间。在这种情况下,您可以在构建TRIE树期间通过指针 将每个单词链接到其所有前缀节点。然后时间复杂度将降低到O(M+N)
,这非常快。但另一方面,它将占用O(NM)
的空间复杂度。假设你有一百万字,平均有5个字母,那么你就会在指针上花费大约150KB的内存。