在前缀树中获取具有公共前缀的所有单词时的性能问题

时间:2013-12-23 23:19:12

标签: c++ performance algorithm prefix-tree

我有一个前缀树来存储大量的单词。现在,如果我想找到所有带有公共前缀的单词“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 解决了!!!我的实施实际上存在问题。我在递归调用中传递了这个巨大的向量字符串容器,这实际上是问题所在。谢谢大家的好意见。

1 个答案:

答案 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的内存。