【问题标题】:Data structure for querying whether a given subset exists in a collection of sets用于查询给定子集是否存在于集合集合中的数据结构
【发布时间】:2011-03-06 07:39:06
【问题描述】:

我正在尝试为文字游戏求解器构建数据结构。

我需要存储大约 150,000 组 {A, A, D, E, I, L, P, T, V, Y} 形式的集合。 (它们是规范化的英文单词,即已排序的字符。注意这是一个 multiset,它可以包含两次相同的字母。)

需要有效地获得以下类型查询的是/否答案:是否存在具有给定子集的集合?例如,是否有任何已知单词包含集合 {D, E, I, L, L, P}?

要求:

  • 查询必须快速
  • 数据结构应适合合理的空间量(例如
  • 数据结构不需要实时构建;它是预先计算好的。

是否有任何数据结构可以很好地满足这种需求?这与 StackOverflow 上的 other set matching 问题略有不同,因为目标集实际上是多重集。

【问题讨论】:

  • 听起来你需要查找 anagram 软件的例子。
  • 有趣的是你应该提到这一点;这是一种字谜;但是,我需要找到“近字谜”或部分字谜。即我需要通过重新排列和添加给定池中的字母来找到字谜。

标签: algorithm data-structures set subset multiset


【解决方案1】:

这让我想起了我曾经做过的一个变异的前缀树/trie。略有不同,但它可能会起作用。如果您的界限很大/没有界限,或者您无法将其转换为您的语言(我用 C++ 编写代码),它可能不起作用。

所以基本上,在 trie 中,您通常存储与下一个字母相对应的孩子,但我所做的是存储与每个字母的频率相对应的孩子。

问题基本上是(从我的角度来看)是,“是否有任何集合具有与子集中相同或更多的字母?”例如,如果子集是 { A,D,E,E },那么您需要查找是否存在至少包含一个 A、一个 D 和两个 E 的集合。

所以,对于 trie,你有这样的东西

            Root
           / | \
          / /|\ \
         / / | \ \
        1 2  ... MAX <-- This represents the frequency of "A"
       /|\ ..... /|\
      1..MAX    1..MAX <-- Frequency of "B"
      ...............
      ...............
      ...............
     1 ... ... ... MAX <-- Frequency of "Y"
    /|\ .... .... / | \
   1..MAX ...... 1 .. MAX <-- Frequency of "Z"

基本上所有的 ... 都代表了很多需要很长时间才能显示的内容。 /,|而\代表父子关系,MAX代表一个字母的最大频率

那么你要做的是,你有一个类似的结构(我用 c++ 编写代码):

struct NODE {
    NODE *child[MAX + 1]; // Pointers to other NODE's that represents
                          // the frequency of the next letter
};

当您创建一个节点时,您需要将其所有子节点初始化为 NULL。您可以通过构造函数(在 c++ 中)或 makeNode() 函数(如

)来执行此操作
NODE* makeNode() {
    NODE* n = new NODE;         // Create a NODE
    for(int i = 0;i <= MAX;i++) // For each child
        n->child[i] = NULL;     // Initialize to NULL
};

一开始,trie 只是一个根

NODE* root = new NODE;

当你向 trie 添加一个集合时,你会得到每个字母的频率并遍历 trie。如果在特定节点上,下一个字母对应的子节点为NULL,则只需创建一个新节点即可。

当您搜索 trie 时,您搜索每个节点的所有子节点,这些子节点对应于子集中字母的频率或更大。例如,如果子集有 3 个 A,则搜索所有的 root->child[3] 然后 root->child[4] 然后 ... 然后 root->child[MAX]。

这可能过于复杂和令人困惑,所以 1)如果您认为我没有生气,请评论令人困惑的地方,以及 2)您可能/可能只想找到一个更简单的方法

【讨论】:

  • 我刚刚实现了它,它的构建速度非常快,而且空间效率很高(180k 字约 6MB)。它也适用于许多查询。然而,不幸的是,有些退化的查询只需要遍历很多很多分支。也许一种优化是按照树的最大数量重新排序树的级别,最大限度地减少所需的回溯量。
  • 非常有趣!我想知道当您搜索“给我[ A,Y] 的所有超集”之类的内容时,这将如何工作?
【解决方案2】:

看来您可以尝试使用 KD-Trees 或变体。

要探索的相关主题是多维范围搜索/查询。

警告:我自己没有使用过这些,但我希望您可以通过阅读有关上述主题的一些文献找到有用的东西。

希望对您有所帮助。

【讨论】:

  • 如果我正确理解了这个建议,我们的想法是将每个多组字母视为一个 26 元素向量。子集查询对应于正交范围查询。
  • 做了一些搜索,听起来 26 维范围树正是我所需要的,但实现起来太复杂了!
  • @David:我猜肯定有现成的解决方案。当然,我还没有尝试自己寻找它们。
  • 别打扰了。 26维kd-tree的最坏情况查询时间是O(n^(1-1/26)),基本上是线性的。维基百科文章建议在实践中,N (150,000) 应该远大于 2^k (2^26 ≈ 64,000,000)。
  • @David:你可以组合维度。说合并 A 和 B。C 和 D 等。一旦你得到减少的候选者列表,你就会进行线性搜索/查找不同的多维结构等。基本上将这种方法与不同的方法结合起来,以便在空间之间进行权衡和时间。如果没有您的实际数据和访问模式,最好的办法是建议可能有用的 通用 结构。你问的问题似乎与范围查询密切相关,我实际上很想看到一个更有效的解决方案!
【解决方案3】:

您可能会使用 trie 并将每个集合插入到 trie 中,使用目标子集迭代遍历 trie 以确定是否有匹配的子集。至少我认为我会这样做。

“trie”实际上是为可检索的数据结构设计的,与普通树非常相似,但具有不同排列的节点,例如:

     A
    / \
   AT AN
     / | \
    |  |  AND
   ANN ANY
    |
  ANNA

在上面的示例中,您可以看到这可能对您的情况有用,因为可以像集合一样检索 ANN 和 ANNA。您可能希望使用一些置换代码以及这种类型的 ADT(抽象数据类型)。

查找更多here

【讨论】:

  • 我考虑过尝试,但这种直接的方法并没有真正奏效。考虑只有一个“单词”的特里树,“AANN”。然后,我们查找“ANN”以查看它是否在 trie 中,它不会。我之前确实尝试过这种技术,使用类似 DAWG(有向无环词图)的东西,为每个有效集合添加多条路线,但规模巨大。主要困难在于,对于长度为 m 的子集,您有 O(m!) 种方法可以到达那里——以不同的顺序添加字符。
猜你喜欢
  • 1970-01-01
  • 2022-01-04
  • 2019-03-27
  • 1970-01-01
  • 1970-01-01
  • 2019-09-02
  • 2020-11-30
  • 2015-10-23
相关资源
最近更新 更多