获取与先前定义的字符串集匹配的字符串的所有前缀的高效结构

Efficient structure to get all prefixes of a string that match a previously defined set of strings

假设我有一组前缀如["a", "ab", "ba", "aba", "bbb"].

我也有一个单词表。对于此列表中的每个单词,例如 "abad",我想有效地获取该单词的所有与前缀集中的单词匹配的前缀。在这个例子中,我想作为输出 ["a","ab", "aba"].

我只想使用标准中的结构。

我正在考虑某种树结构,但我想不出一种相对轻松地实现它的方法。

我认为 trie 是最快的方法。但我不知道是否有 std trie。 使用 trie,您可以非常快速地检查前缀。

你的 trie 看起来像这样:

红色节点是端节点...所以从顶部到红色节点是一个前缀。

如果你只会用std的东西,你可以写一个class节点。在这个 class 中你有一个 std::map 到 children。如果你的 trie 中有多个 a 和 b,地图将帮助你快速找到正确的子节点......如果你使用汉字或其他字母......(只有几个元素你最好使用 std::vector)

您还需要一种添加前缀的方法。 之后你只需要一种方法来检查给定的单词是否是前缀,方法是遍历节点和地图并检查我们是否传递了红色节点。

正如已经提到的那样,trie 是开始查找的开始,一些 Radix tree may be, however there are a lot of options, but, unfortunately, non of them is explicitly in std. If you want to have something quick and practical, I know this article 它是针对 C# 描述的,但在 C++ 中很容易实现。

一个简单的实现是使用 std::unordered_set 的散列 table,然后将列表中每个单词的每个前缀与集合进行比较。

#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>

int main ()
{
    std::unordered_set<std::string> myPrefixes = {"a", "ab", "ba", "aba", "bbb"};

    std::vector<std::string> listOfWords;

    for (int i = 0; i < listOfWords.size(); i++) {
        std::vector<std::string> result;
        std::string word = listOfWords[i];
        for (int j = 0; j < word.length() - 1; j++) {
            if (myPrefixes.count(word.substr(0, word.length - j)) > 0) {
                result.push_back(word.substr(0, word.length - j);
            }
        }
        // print the result vector or do whatever with it
    }
    return 0;
}
  1. 将前缀存储在 std::vector<std::string>
  2. std::sort()向量
  3. 对于每个感兴趣的单词,使用 std::mismatch() 来查找它们不匹配的字符,如果所有前缀都匹配,那么你就有一个匹配项
  4. 如果部分匹配,中断

您不需要任何特殊的数据结构。它会很快吗,可能不如特里那么快,它会工作吗 - 最有可能。它是否有效 - 可能 - 取决于前缀和单词。

这个答案通过使用 trie 来获得性能增益而增加了一点复杂性,这可能是非常巨大的。假设 M 个前缀词,每个词连同要搜索的词平均 N 个字母,使用 set 的上述过程的复杂性需要 O(N^2) 时间来查找 [= =12=]字母词。 Trie 方法,对于初始 O(MN) 预处理成本,可以将搜索降低到 O(N)。

有趣的是,Trie 构建部分(下面的 Node 结构和 insert 函数)实际上只需要 7 行。

struct Node
{
    bool is_Leaf = false;
    unordered_map<char,Node*> transitions;
};

void insert(Node* node,char* str,char* end){
    if(str==end) {node->is_Leaf = true;return ;}
    if (node->transitions[*str]==nullptr) node->transitions[*str] = new Node();
    insert(node->transitions[*str],str+1,end);
}

void search(Node* root,char* str,char* end,string so_far=""){
    if(root && root->is_Leaf) cout<<so_far<<endl;
    if(!root or str==end) return;
    so_far.push_back(*str);
    search(root->transitions[*str],str+1,end,so_far); 
}

int main(int argc, char const *argv[])
{
    Node* root = new Node();
    vector<string> prefixes = {"a", "ab", "ba", "aba", "bbb"};
    for(auto& s:prefixes) insert(root,&s[0],&s[0]+s.size());

    string to_search = "abad";
    search(root,&to_search[0],&to_search[0]+to_search.size(),"");
    return 0;
}