搜索引擎--------倒排表数据结构、通配符查询、拼写纠正详解

目录:算法

  • Dictionary Data Structure  词典数据结构
  • Wild-Card Query  通配符查询
  • Spelling Correction  拼写纠正

 


 
搜索引擎里的 dictionary data一般存储着这些信息:
  • 索引词term vocabulary)。
  • 文档频率document frequency,即这个词在多少个文档里出现)。
  • 指向倒排表的指针pointers to each postings list )。
那么,他是怎样的一个数据结构呢?
 
 
一种很是 naive的词典结构就是:
其中,term的类型是char[20],占20bytes,document frequency类型int,占4-8 bytes,pointer指针占4-8 bytes。
这种数组的存储方式会有两个问题:
空间上:  How do we store a dictionary in memory efficiently?
时间上:  How do we quickly look up elements at query time?
 
固然,比较好的数据结构是:
1,Hashtables。2,Trees。
(一些搜索引擎用的是Hashtables,一些用的是trees。)
其实,具体判断该选择什么样的数据结构,主要从如下三点来衡量:
1,数据是否是持续增加的?
2,访问查找是否是很频繁?
3,词典的规模是否是很大?
 
具体来看看Hashtables和Trees。
若是用的是 Hashtables,那么每个索引词都会被一个hash函数转换成一个整数。
优势:
查找起来十分迅速,时间是O(1)。
缺点:
若是两个词相差十分的小,不容易发现。好比说  judgment/ judgement 。
没法实现前缀查询。即查询全部给点前缀的词。
若是词典是持续增加的,须要时不时的对原来的hashtables从新进行hash计算,这个代价是很大的。
Example:
 
 
若是数据结构是树的话。最简单的就是二叉树了。
树能够支持前缀查找。搜索速度略低于哈希表方式,时间是O(logM),其中M是词汇表的大小,即全部词汇的数目。
O(logM)仅仅对平衡树成立。可是使二叉树保持平衡的开销很大。
如何解决保持平衡的开销问题呢?
B-树!!!
首先看B-树的定义是这样子的: B-树是一种平衡的多路查找树。

一棵m 阶的B-树知足下列特性的m 叉树:
数组

  • 树中每一个结点至多有m 棵子树;

 

  • 若根结点不是叶子结点,则至少有两棵子树;
  • 除根结点以外的全部非终端结点至少有[m/2] 棵子树;
  • 全部的非终端结点中包含如下信息数据:(n,A0,K1,A1,K2,…,Kn,An)。其中:Ki(i=1,2,…,n)为关键码,且Ki<Ki+1,Ai 为指向子树根结点的指针(i=0,1,…,n),且指针Ai-1 所指子树中全部结点的关键码均小于Ki (i=1,2,…,n),An 所指子树中全部结点的关键码均大于Kn。n为关键码的个数
  • 全部的叶子结点都出如今同一层次上,而且不带信息(能够看做是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。

这样讲起来或许比较枯燥难懂,看这张图就行了:数据结构

树的优势就是能够解决前缀查找的问题了。函数

缺点是速度比哈希慢点,是O(logM)而且要求是平衡的,从新平衡一棵树的代价大。(虽然B-树减轻了这种代价)post

 


 

 

Wildcard queries,通配符查询。

好比查询语句 mon*:找出全部以mon开头的单词。若是采用树(或者B-树)结构词典,咱们能够很容易的解决,只须要查询范围在mon ≤ w < moo的全部单词就ok了。ui

可是查询语句 *mon:找出全部以mon结尾的单词就比较困难了。其中一种办法就是咱们增长一个额外的B-树来存储全部单词,以从后向前的顺序,而后在这个树上查询范围在nom ≤ w < non的全部单词。搜索引擎

但是如何处理通配符在单词中间的查询呢?google

好比query是co*tion的话。咱们固然能够分别在B-树查询到co*和*tion的全部单词而后合并这些单词,可是这样开销太大了。spa

解决办法就是:轮排索引(Permuterm Index)咱们把query的通配符转换到结尾处。指针

设置一个标志$表示单词的结尾。

以hello举例,hello能够被转换成hello$, ello$h, llo$he, lo$hel, o$hell。$表明中hello的结束。如今,查询X等于查询X$,查询X*等于查询X*$,查询*X等于查询X$*,查询X*Y等于查询Y$X*。对于hel*o来讲,X等于hel,Y等于o。

既然咱们已经把通配符都弄到了单词尾部,如今咱们又能够经过B-树像之前那样查询拉。

以上,咱们已经完成了对query的转换,那么那些存储的索引的词要怎么处理才能配合这种query查询呢?

咱们对索引来创建索引!!

Bigram indexes。就是两两个字母来索引。

举例来讲,一个文本是“April is the cruelest month”,分别成Bigram indexes就是“$a,ap,pr,ri,il,l$,$i,is,s$,$t,th,he,e$,$c,cr,ru,ue,el,le,es,st,t$, $m,mo,on,nt,h$”,其中$ 表明着单词边界的符号。

那么如何对索引创建索引??

维护第二个倒排表,倒排表的索引词是Bigram indexes,posting list的值就是与之匹配的dictionary terms。

像这样:

 

好了!目前为止,咱们既对query进行了处理,也对terms进行了处理。

因此如今若是咱们要查询 mon*,query会被分解成 $m AND mo AND on ,而后从上图中的倒排表作两个AND能够获得匹配的terms了!!

可是,咱们会发现 $m AND mo AND on 也会匹配到单词moon,而moon不符合mon*的格式,这是他的一个缺点。咱们必需要过滤掉这些词。

另外,一条查询语句每每至关多的布尔查询,这个开销也挺大的。

 


 

Spelling correction,拼写校订。

咱们google一下 Alanis Morisett ,获得结果如图:

对了,就是这个提示,您找的是否是:xxxxxx。

在搜索引擎中,须要有一个能够查询到全部正确单词的词典。

给定一个词典和一个query,返回一个和query最接近的words。

怎么用才算是最接近??

  • 编辑距离算法。
  • 带权编辑距离算法。

编辑距离(Edit distance)

给定两个字符串S1和S2,从S1转换到S2的最小步骤就是他们的编辑距离。这些步骤包括,Insert(1步), Delete(1步), Replace(1步),copy(0步)

好比说:

dof到dog的编辑距离是1,cat到act的编辑距离是2,cat到dog的编辑距离是3.

算法导论里关于编辑距离的伪代码以下:

 

举例子:算cats到fast的编辑距离~

括号里圈出来的表示实际应该填的值,其余的只是用来进行对比,取其中最小的数。

具体到表中的每一格中四个数字的含义就是:

 

从左边的格子过来表明增长,上边的格子过来表明删除,斜上角的格子过来表明替换(此时两个字符不相等)或复制(此时两个字符不相等)。

编辑距离就是这样子。那么什么是带权编辑距离呢?

好比说,咱们打字的时候,m被错打成n的概率会比m错打成p的概率更大,因此咱们应该认为m和n的编辑距离小于m到p的编辑距离。所以将m替换为n时计算编辑距离应该比将m替换为p时的编辑距离小。

实现带权编辑距离,咱们须要一个额外的权值矩阵。

那么,给定一个查询词,咱们是否是得计算这个查询词和全部的索引词之间编辑距离呢?

答案是否认的,由于这样开销很大并且慢。

怎么用减小计算呢??

好比说,若是query是lord:

咱们只取lo,or,rd中有重叠两次或以上的term,而后合并这些term,以这个为范围进行编辑距离的计算。

 


 

若是本文对您有一点点的帮助,请帮忙点个赞,十分感谢!~ =。=

若是您有更好的看法或纠正,欢迎您在下面评论指出。

相关文章
相关标签/搜索