我想创建一个图书馆书籍的数据库,可以有效地搜索子字符串匹配。 也就是说,如果我搜索“编程”,那么将返回包含单词编程的所有书名。 该数据库可以预处理,并将完全存储在内存中。
什么是有效的数据结构和搜索算法来解决这个问题? 我想在C ++中完全实现它,所以请不要使用第三方库。
答案 0 :(得分:6)
Suffix tree是子字符串搜索的有效数据结构。
这个想法是:
创建后缀树数据结构,并从每个叶子连接到与此后缀所代表的书籍相关的条目。
在查询时间 - 使用子字符串遍历树 - 并从您到达的终点(最长匹配) - 执行一些遍历(例如DFS)并检索与所有后缀相关的所有条目query是。的前缀。
当然,如果你只想要单词而不是所有的子串,一个地图(基于树/散列)可能就足够了,并且更容易实现和使用(该类型应该是map<string,list<book> >
,例如基于树的方法,它将从每个单词映射到包含标题中包含该单词的所有书籍的列表。
您也可以使用trie来实现地图。
答案 1 :(得分:2)
对于子字符串匹配,有一个简单的方案:在“块”中拆分完整标题,并按以下方式创建数据库:
当用户查询系统时,以相同的方式分割她的请求以识别匹配的书籍。
通过这个简单的方案,您可以获得2点功能定制:如何派生块以及如何对书籍进行排名;和1点技术定制:如何“合并/加入”不同匹配块的集合,这取决于你想要对书籍进行排名的方式。
如何推导大块?
一种简单(但有效)的方式是分割单词边界:The C++ Programming Language
变为{the, c++, programming, language}
。
注意:经常会忽略一些单词(黑名单)。例如,The
可能出现在80%的标题中,因此在大多数情况下考虑它是没有用的。
注意:搜索可能不区分大小写。
如何排名?
一个天真的算法是返回所有匹配。更好的方法是根据查询中与该ID匹配的块数对它们进行排名。一个更好的方法是将那些单词出现的标题排在较高的位置,而查询的顺序与查询相同(最长的子匹配)。当然,你应该考虑同义词。
排名可能是系统的核心,谷歌很受欢迎,因为它的排名算法运行良好意味着如果找到你想要的东西。
如何实施合并/加入?
除非您只想返回与原始查询中的所有块相匹配的搜索结果(这很有用,但因为同义词而烦人),那么您应该保留有序集并为每个创建它们的交集。块:
chunk1
:{B1, B2, B7, B9, B15}
chunk2
:{B1, B7, B8, B13, B15}
chunk3
:{B1, B3, B4, B7, B9, B12, B13, B14, B15}
然后,与chunk1
和chunk2
的集合相交,前往{B1, B7, B15}
并与chunk3
相交(不会改变任何内容)。
注意:从较小的集合开始,您可以保留较小的中间结果,从而加快结果。
注意:当一个较小的集合与一个更大的集合相交时,较大集合的线性行走可能比二进制搜索慢得多。
另一方面,如果您想对搜索结果进行排名,那么您可能需要将地图ID作为中间结果保留 - &gt;得分了。该映射可以是二叉搜索树或散列映射(后者对于非常大的集合来说速度更快,但对于小型集合而言则有一些开销)。
请注意,这种排名的东西通常很慢,但很容易并行化。这就是Google对MapReduce所做的事情。