【问题标题】:data structure for probable string matches可能的字符串匹配的数据结构
【发布时间】:2013-08-04 05:37:27
【问题描述】:

以下操作的最佳数据结构是什么:
数据结构存储单词列表
input:我们将其命名为'pre'的字符串
output:所有具有pre的字符串的列表作为它们的前缀(来自存储的单词列表),列表中的单词应按优先级降序排列。
如果在作为输出返回的字符串列表中使用特定字符串,则其优先级会增加。
我将使用它进行单词预测,因此每次用户选择某个单词(从返回的单词列表中)时,它的优先级都会增加 1。
我已经实现了一个尝试,但它按字母顺序给出了输出(列表),我希望它按优先级排序。

【问题讨论】:

  • “以前缀为前缀的所有字符串的列表”——这不是无限的吗?
  • "如果在作为输出返回的字符串列表中使用特定字符串,则其优先级会增加。"你能详细说明这意味着什么吗?另外,您是否正在处理未排序的单词列表?您的问题需要更多信息。
  • 我想像某种形式的 trie。

标签: string algorithm data-structures


【解决方案1】:

解决您的问题的最佳数据结构是trie,trie 将允许以空间为代价进行快速查找。

点击此链接了解更多信息:link

【讨论】:

  • 哈希函数将使用整个输入字符串将其映射到索引上。您将无法单独使用前缀查询散列函数并获取具有该前缀的整个字符串。编辑:但是尝试肯定会奏效!
  • 我不仅想要快速查找,而且我想要一个返回单词列表的数据结构,其中单词按优先级排序。我已经实现了一个尝试
【解决方案2】:

这可能不是最好的解决方案,但也许它会给你一些想法。

使用 trie 存储所有单词,并让您的节点包含优先级字段。

具有某种列表数据结构,您的 trie 可以看到它与您的查询函数具有相同的范围。该列表将包含(单词,优先级)条目。

迭代输入单词下方的树(因此“pre”下方的子树)并查找所有单词(可能节点具有布尔“单词”字段或其他内容)。当找到一个单词 (word == 1) 时,您将把 (word, priority) 添加到列表的末尾。

假设 i 是新条目的位置,然后将 list(i) 与 list(i - 1) 进行比较。如果 list(i - 1) 的优先级小于 list(i) 则您切换它们的位置。继续这样做,直到第 i-1 个项目的优先级等于或高于新添加的项目。

一旦 trie 搜索功能完成,您将获得一个列表,其中包含(单词,优先级)条目按降序排列。

我希望这是有道理的。

【讨论】:

  • 所以,基本上你所说的是从 trie 中获取单词以及它们的声誉,然后对它们进行排序。如果我错了,请纠正我。
  • 是的,我想不出任何其他方法。
【解决方案3】:

正如其他答案所示,您可以使用 trie 快速获取具有给定前缀的所有单词,然后根据单词的优先级对单词进行排序。忽略从 trie 中获取匹配词的访问时间,如果你得到k 匹配词然后根据优先级排序需要O(k log k) 时间。这非常接近理论上最优的O(k) 时间,您可能不想费心尝试在实际应用中对其进行改进,尤其是因为在排序后打印k 单词实际上具有运行时间O(kl) 其中@987654326 @ 是匹配词的平均长度,l 的乘数可能通常与 log k 大致相同的顺序。但是,如果您愿意将使用的空间量乘以 O(L_avg),其中 L_avg 是所有单词的平均长度,那么您可以获得按排序顺序访问单词并更新优先级 +1 的时间到O(k + L log n),其中L 是优先级+1 的所选单词的长度,n 是您的总单词数。

这个想法一开始可能听起来有点疯狂,但请耐心等待,我会解释,内存真的只会乘以O(L_avg)。这个想法是,在 trie 的每个节点上,将所有具有相应前缀的单词及其优先级存储在自平衡二叉搜索树中(根据优先级排序)。您可以将单词表示为存储单词的数组的索引,而不是完整的单词,因此 trie 的每个节点的存储需求与具有相应前缀的单词数量成线性关系。当一个词获得优先级+1时,您必须向上遍历trie并更新该词及其所有父节点的对应trie节点的平衡二叉搜索树,这需要O(L log n)时间。但是,要按排序顺序获取单词的索引以响应查询,您所要做的就是按顺序遍历二叉树,这需要O(k) 时间。现在关于存储。长度为L 的单词存储在L 二叉树中:单词的trie 节点树以及其所有L-1 父节点的树。因此,如果您将树中所有节点的所有树的总存储量相加,通过计算每个单词在树中出现的次数,树的总存储量与所有单词的总长度呈线性关系,即O(n L_avg)。如果您可以在存储上处理该乘数,那么我相信这是理论上最快的处理查询和优先级更改的方法,如果您真的想删除通过排序查询结果获得的 log k 乘数。

【讨论】:

    【解决方案4】:

    这是内存效率低的解决方案。

    参考:Trie example

    在每个节点存储所有可能的有效后缀,这些后缀具有到目前为止遍历的父节点的前缀。同样对于快速检索,使用基于优先级的最大堆来存储这些后缀。

    例如你的 c++ trie 节点会是这个样子(我没有测试过代码)

    typedef pair<int, string> SUFFIX;
    class Compare {
    public:
        bool operator() (SUFFIX &d1, SUFFIX &d2) {
            return d1.first < d2.first;
        }
    };
    typedef priority_queue<SUFFIX, vector<SUFFIX>, Compare> max_heap;
    
    struct TrieNode {
        char data; // char at current node
        max_heap word_suffixes; 
        bool is_complete;
    };
    
    /* Note: max_heap word_suffixes basically hold all strings without prefix so far. 
    For example: You have dictionary of egg, eye at the starting node "e" your max
    heap will have two entries "gg" and "ye" (with highest priority say "gg" 
    as root of max heap) 
    */
    

    现在时间复杂度为

    1) 按照前缀“pre”遍历trie O(L) (L=len of pre)

    2) 在节点从最大堆中弹出每个字符串,这将为您提供按优先级排序的列表。 O(nlogn) (n=堆大小)

    3) 在增加已用词的优先级后重建堆。 O(nlogn)

    注意:您也可以尝试将后缀存储为 BST,并将优先级作为键。有序遍历将为您提供按优先级排序的后缀列表 (O(n))。可以通过从 BST 中删除后缀并重新添加新的优先级来增加使用过的单词的优先级(对于平衡的 BST,插入/搜索/删除将为 O(height))。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-13
      • 2017-01-09
      • 2023-03-12
      • 1970-01-01
      • 2015-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多