我正在寻找一种算法来在合理的时间内解决以下问题。
给定一组集合,找到所有这些集合,这些集合是给定集合的子集。
例如,如果你有一组搜索术语,如[“堆栈溢出”,“foo bar”,...],那么给定一个文档D,找到所有搜索词,它们的所有词都出现在D中。
我找到了两个足够的解决方案:
使用位向量列表作为索引。要查询给定超集,请为其创建位向量,然后迭代列表,对列表中的每个向量执行按位OR运算。如果结果等于搜索矢量,则搜索集是由当前矢量表示的集合的超集。此算法为O(n)
,其中n是索引中的集合数,按位OR非常快。插入时间为O(1)
。警告:为了支持英语中的所有单词,位向量需要数百万位长,并且需要存在单词的总顺序,没有间隙。
使用前缀树(trie)。在将它们插入到trie之前对它们进行排序。搜索给定集时,请先对其进行排序。迭代搜索集的元素,激活匹配的节点,如果它们是根节点的子节点或先前激活的节点的子节点。通过激活节点到叶子的所有路径表示搜索集的子集。此算法的复杂性为O(a log a + ab)
,其中a
是搜索集的大小,b
是索引集的数量。
你的解决方案是什么?
答案 0 :(得分:2)
如果这些集合与总词汇量相比稀疏,那么前缀trie听起来像是我要尝试的东西。不要忘记,如果两个不同前缀的后缀集相同,则可以共享表示后缀集的子图(这可以通过哈希值而不是任意DFA最小化来实现),给出DAG而不是树。首先尝试排序最少或最频繁的单词(我敢打赌一个或另一个比一些随机或字母顺序更好)。
对于第一个策略的变体,你用一个非常大的整数(位向量)表示每个集合,使用稀疏有序集合/整数映射(跳过连续0的运行的位序列上的trie) - http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.37.5452(在http://www.scala-lang.org/docu/files/api/scala/collection/immutable/IntMap.html中实施)。
如果你的参考集(集合)是固定的,并且你想找到那些包含其他集合的许多集合,我会计算直接的包含关系(带有a->的路径的有向非循环图; b iff b包含在a中,并且没有冗余弧a-> c,其中a-> b和b-> c)。分支因子不超过集合中元素的数量。从给定集合可到达的顶点正是它们的子集。
答案 1 :(得分:0)
首先,我将构建2个数据结构S和E.
S是一组数组(集合S有N个子集)。
S[0] = set(element1, element2, ...)
S[1] = set(element1, element2, ...)
...
S[N] = set(element1, element2, ...)
E是列表的映射(索引的元素散列)。每个列表都包含S-indices,其中元素出现。
// O( S_total_elements ) = O(n) operation
E[element1] = list(S1, S6, ...)
E[element2] = list(S3, S4, S8, ...)
...
现在,2个新结构,设置L和数组C.
我将存在于E中的D的所有元素存储在L.(O(n)操作)中
C是计数器的数组(S指数)。
// count subset's elements that are in E
foreach e in L:
foreach idx in E[e]:
C[idx] = C[idx] + 1
最后,
for i in C:
if C[i] == S[i].Count()
// S[i] subset exists in D
答案 2 :(得分:0)
您可以为您的文档构建索引吗?即从每个单词到包含该单词的那些文档的映射。一旦你构建了它,查找应该非常快,你可以设置交集来查找匹配所有单词的文档。
这是full text search上的Wiki。
编辑:好的,我倒退了。您可以将文档转换为集合(如果您的语言具有set数据类型),请对搜索执行相同操作。然后,测试一个是否是另一个的子集就变得很简单。
在幕后,这实际上是同一个想法:它可能涉及为文档构建哈希表,散列查询,并依次检查查询中的每个单词。这将是O(nm),其中n是搜索次数,m是搜索中的平均字数。