我正在构建一个数据结构,帮助索引总长度为n的S文档集合,这样它就支持以下查询:给定两个单词P1和P2, count 包含所有文档P1但不是P2。我希望答案是完整的(不要错过结果)。
我构建了一个通用后缀树,并选择每个第(n)个叶子及其祖先(并删除每个单个节点)。对于每个内部节点v,我预先计算针对节点u的查询的答案。
但是,如果查询包含在节点v和u中出现在树中的单词,我可以在O(1)中得到答案,但是当单词不在其中一个节点中时,我该怎么办?我们选了?
我可以通过保留O(n 2 )数据结构并进行预处理并将所有可能的答案准备好进行O(1)时间检索来轻松完成,但目标是构建O(n)空间中的这种数据结构,使查询尽可能高效。
答案 0 :(得分:2)
听起来inverted index对您仍然有用。它是包含它们的有序文档列表中的单词映射。这些文档需要有一个共同的总排序顺序,它按照这个顺序显示在每个单词桶中。
假设您的n
是单词出现次数中的语料库总长度(而不是词汇量大小),则可以在O(n log n)
时间和线性空间中构建它。
鉴于P1
和P2
,您需要进行两次单独的查询以分别获取包含这两个术语的文档。由于这两个列表共享一个共同的排序,您可以执行类似线性合并的算法,只选择包含P1
但不包含P2
的文档:
c1 <- cursor to first element of list of docs containing P1
c2 <- cursor to first element of list of docs containing P2
results <- [] # our return value
while c1 not exhausted
if c2 exhausted or *c1 < *c2
results.append(c1++)
else if *c1 == *c2
c1++
c2++
else # *c1 > *c2
c2++
return results
注意循环的每次传递都至少迭代一个游标;它以两个初始查询的大小总和的线性时间运行。由于只有c1
光标中的内容输入results
,因此我们知道所有结果都包含P1
。
最后,请注意我们始终只推进“滞后”游标:这(以及整个文档排序)保证如果文档出现在两个初始查询中,则会有循环迭代,其中两个游标都指向该文档。当这个迭代发生时,中间的子句必然会启动并通过推进两个游标来跳过文档。因此,包含P2
必须的文档不会添加到results
。
此查询是一个名为布尔查询的通用类的示例;可以扩展此算法以涵盖大多数任何布尔表达式。某些查询会破坏算法的效率(通过强制它遍历整个词汇空间)但基本上只要你不否定每个术语(即不要求not P1 and not P2
)你就没事了。请参阅this进行深入治疗。