【问题标题】:Efficient structure to get all prefixes of a string that match a previously defined set of strings获取与先前定义的字符串集匹配的字符串的所有前缀的有效结构
【发布时间】:2017-05-30 12:59:46
【问题描述】:

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

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

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

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

【问题讨论】:

  • trie 怎么样?
  • 这将是标准正则表达式的完美案例
  • @Someprogrammerdude 如何仅使用 std 在几行代码中实现 trie?

标签: c++ string match prefix


【解决方案1】:

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

#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;
}

【讨论】:

  • 谢谢!快速评论:您只需要搜索最大长度前缀或单词的最小长度
【解决方案2】:
  1. 将前缀存储在std::vector&lt;std::string&gt;
  2. std::sort()向量
  3. 对于每个感兴趣的单词,使用std::mismatch() 来查找它们不匹配的字符,如果所有前缀都匹配,那么你就有一个匹配
  4. 如果部分匹配,则中断

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

【讨论】:

    【解决方案3】:

    这个答案通过使用 trie 来提高性能会增加一点复杂性,这可能非常巨大。假设 M 前缀词,每个词与搜索词的平均 N 字母一起,使用 set 的上述过程的复杂性需要 O(N^2) 时间来查找 N 字母词的前缀。 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;
    }
    

    【讨论】:

      【解决方案4】:

      我认为尝试是最快的方式。但我不知道是否有标准特里。 尝试一下,您检查前缀的速度非常快。

      你的 trie 看起来像这样:

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

      如果你只能使用标准的东西,你可以写一个类节点。在这堂课中,您有一个 std::map 给孩子们。如果您的 trie 中有多个 a 和 b,则该地图将帮助您快速找到正确的子节点……如果您使用汉字或其他字母……(只有少数元素,您最好使用 std: :向量)

      您还需要一种添加前缀的方法。 之后,您只需要一个方法来检查给定单词是否是前缀,方法是遍历节点和映射并检查我们是否通过了红色节点。

      【讨论】:

        【解决方案5】:

        正如已经提到的,trie 是开始寻找的开始,一些Radix tree 可能是,但是有很多选项,但不幸的是,它们都没有在 std.xml 中明确显示。如果你想要一些快速实用的东西,我知道 this article 它是为 C# 描述的,但在 C++ 中很容易实现。

        【讨论】:

          猜你喜欢
          • 2022-06-10
          • 1970-01-01
          • 2011-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-28
          相关资源
          最近更新 更多