Wu-Manber 经典多模式匹配算法

多模式匹配的用法,多了去了!DB中对selected patterns进行数挖;安全中对suspicious keyword进行匹配;各种日期形式2009-5-20,2009年5月20日,May,20的搜索;DNA配对;各种replace功能;等等,太口水了枚举这个。

       Wu-Manber基于BM算法思想,如果您佬BM还没OK,请参照我的BM日志搞搞清楚先。

       提到Wu-Manber,其实就是SHIFT、HASH、PREFIX三张表,预处理patterns先把这三张表填好,搜的过程忒简单。

 

1.  拿表说事儿:表干嘛用的

先有个大概印象:SHIFT[]就一跳转表,一张记录向右滑动距离的表。当SHIFT[i]=0时,说明那么多patterns肯定有匹配上的,这时HASH[]和PREFIX[]就要站出来指明谁暂时匹配上了,并对它们通通匹配一番。

 

2.  预处理这一堆patterns:填上三表

这堆patterns长度不一,m=最小长度,该算法也就只检测每pat前m个字符了(那剩余的怎么办呢?o还没想明白)。Patterns长度基本相当最好,假如有一pat掉链子,特短(长度为2),那么每次滑动距离最多也就才2,还滑个什么劲儿呀。所以各pat长度要统一要和谐。

我们把text切成长度为B的块(一般2或3),B怎么算呢?假如有k个pat,那么各pat总长M=k*m,|∑|为c(所用字符集大小),那么B=logc(2M)。既然这样,那SHIFT滑动距离就不由末尾单个字符而由末尾B个字符而定了。如果这B个字符跟所有pat都没匹配上,那么很自然小指针可以后滑m-B+1。为什么是m-B+1呢?

 

 

如上图示,虽然XAB不出现在pat前端,但Text(待匹配)中的B分串“XAP”的后缀AB是pat前缀,如果你索性移m=5,那么就错过一个匹配了。其实如果B块不出现在pat末,那么也许它的B-1后缀可能出现在pat前缀,因此,安全的移动距离为m-(B-1)=m-B+1。这其实是相对保守的策略,最后我们会提出改进算法。

 

2.1  SHIFT[]

开SHIFT[]表大小为|∑| B,因为要组建长度为B的str,其中每个字符均有|∑|种选择。通过hash function,每个str计算得到一个值index而住进SHIFT。我们现在扫描Text的X=x1…xB, hashFunc(X)=h,那应该滑动多少呢?

如果X跟各pat都没匹配上,那么SHIFT[h]=m-B+1。如果X跟其中某些pat匹配上,那么SHIFT[h]=m-q,其中q是X出现在最右的pat中(假如patj)的xB位置,显然它是最小滑动距离。其实,不用等到实际考查Text时再计算SHIFT,我们预处理各pat就可以把SHIFT表填上。对于每个pat,计算其每个长度为B的subpat(aj-B+1…aj)的值,SHIFT[hashFun(subpat)]=min{m-B+1,m-j}。

哈哈,于是乎SHIFT[]里记录了Text能安全滑过的最大距离,滑的越大当然越快,但是滑的小一点倒也没错。根据这一点,我们可以压缩SHIFT表,把不同的subpat压缩进一个entry,只要值留最小的那个就OK了。(在agrep这个Wu-Manber算法的应用中,B=2时用的原始SHIFT表,B=3时用的压缩SHIFT表)。

 

2.2  HASH[]

只要SHIFT[]>0,那尽管滑text就好了(100个pat的应用中,5%遇到0,1000个时候27%,10000个时候53%)。如果=0,总不至于去跟所有pat一一比对看谁暂时匹配上了吧?o们文明人要用更懒更聪明的方式,去快速锁定那些暂时匹配上的pat们。怎么做呢?HASH!

HASH[i]存放一个指向链表的指针,链表存着这样的pat(末B位通过hash function计算是i)。HASH[]表大小同SHIFT,但相对就稀疏多了,SHIFT那儿存着所有可能的组合的SHIFT值。哎,牺牲空间换时间,时空一向两难全。

记:现正扫描的Text的末B位的哈希值为h。同时引入PAT_POINT(指向实际patterns存储位置的指针链表),该链表按patterns末B位的哈希值排序,那么HASH[h]的值是p(p指向PAT_POINT中哈希值为h的第一个结点处),此时相当于找到第一个跟text末B位匹配上的pat,那么进行匹配,如果匹配不上就继续p++,一直到HASH[h+1]指向的那处地址截止。以上是SHIFT[h]=0的情况。对于SHIFT[h]≠0,那么HASH[h]=HASH[h+1],因为没哪个pat的末B位能匹配上,自然这两个HASH值应该相等(开始就是结束)。这样,就填好整张HASH[]表了。

 

2.3  PREFIX[]

       如果只有HASH[]表,就囧大了。例如自然语言text中以ing,ion结尾的单词非常多,pat中出现ing/ion结尾的也非常多,如果按HASH[]的方法,那就得HASH[h]~HASH[h+1]的pat一个个匹配。能不能更快些呢?引入PREFIX[]。

       对每一个pat,除了记录其末B位字符的哈希值(PAT_POINT),我们还要记录其首B’位字符的哈希值(PREFIX),一般取B’=2。这是一种有效的过滤手段,因为既末B位相同前B’位也相同的pat很少,这样就没那么多pat需要去匹配了(不像上面那种要一一匹对)。但是也需要权衡啦,因为你计算PREFIX哈希值需要时间,存储它需要空间,换回“一一匹配”的时间,不知划不划算。

 

3.  匹配过程

Step1. 现正扫描的text的末B位tm-B+1…tm通过hash function计算其哈希值h。

Step2. 查表,SHIFT[h]>0,text小指针后滑SHIFT[h]位,执行1;SHIFT[h]=0,执行3;

Step3. 计算此m位text的前缀的哈希值,记为text_prefix;

Step4. 对于每个p(HASH[h]≦p<HASH[h+1])看是否PREFIX[p]=text_prefix。如果相等,方才让真正的pat(即PAT_POINT[p])去和text匹配。

 

计算SHIFT[],HASH[],PREFIX[];

//开始匹配

while(text<textend)

{

         hashVal=hashBlock(text);//计算当前块的哈希值

         //查找块的坏字符移动表(SHIFT)得到下一个匹配开始位置

         shift_distance=SHIFT[hashval]; //③

         if(shift_distance==0) //当前块出现在某pat末

         {

                   shift_distance=1; //①

                   p=HASH[hashval];

                   //得到可能与当前块匹配的所有pat的集合的开始位置

                   while(p)

                            检验子集中的pat是否匹配;

         }

         text+=shift_distance; //② 选择下一个可能的匹配入口

}

 

这里用C给出main(),源码(glimpse代码的一部分)可以从FTP下载:cs.arizona.edu

 

 

 

复杂度O(BN/M),B、M含义同前,N是text字符数。Patterns很短或很少的时候,Wu-Manber不是很牛叉。而成千上万的patterns一起匹配过去,Wu-Manber牛气冲天了。

 

3.  Wu-Manber总结与改进

       一言以闭之:WM是BM处理多模式的派生形式。用的BM算法框架,用块字符来计算的坏字符移动距离(SHIFT[]);在进行匹配的时候,用的HASH[]选择patterns中的一个子集与当前文本进行匹配,减少无谓的匹配操作。WM不会随着patterns的增加而成比例增长,它远少于使用每一个pat和BM算法对文本进行匹配的时间总和。

 

改进1:

       但是,上文提到过安全的移动距离最大是m-B+1,这是相对保守的策略。其实像图1那种情况的出现次数相对于坏字符情况的出现次数,那真是小乌见大乌了。所以,一次只滑m-B+1多慢呀,能滑m才够快够刺激。呵呵,怎么办呢?想想BM中怎么做地。我们将SHIFT作为坏字符转移表,这样算:

 

 

 

这样,坏字符转移函数的值域0<=y<=m,而不是原来的0<=y<=m-B+1了。下表是SHIFT表的两个版本的实现。SHIFT表空间不变,时间上多了个for循环,总时间是O(B*|∑|+M),|∑|是块集中块的个数。

 

原始实现:

 

用m-B+1填写SHIFT表;

for each pat

{

         for pat中每一个块

                   计算SHIFT[Bc];

}

 

 

 

改进的实现:

 

用m填写SHIFT表;

for(i=1;i<B;i++)

{

         对所有Bc∈[suffix(Bc,i)=prefix(pat,i)]

                   SHIFT[Bc]=m-i;

}

for each pat

{

         for pat中每一个块

                   计算SHIFT[Bc];

}

 

改进2:

       WM算法一旦找准匹配入口点,就开始进行“逐个”的比较,能不能用“好后缀”呢?呵呵,可以。引入GBSShift[],该表记录了每pat的末B位在所有patterns中的所有非后缀出现位置与相应pat_end的距离的最小值。这样就可以快速确定滑动距离。

 

 

 

       GBSShift[]与SHIFT[]大小相同,|∑|。GBSShift[]和SHIFT[]表计算近似,不需额外计算工作,只需复制计算出的SHIFT[]表计算结果,所以它所需的额外时间是O(|∑|)。将WM伪码中的//①改为shift_distance=GBSShift[hashval]即可。

       结合改进1、2,改进算法在处理大规模数据时比WM算法所用时间养活了8~15%。

 

盘点一下:

AC 算法被用于fgrep1.0(在UNIX中通过-f使用)

BM_AC算法在gre中应用,并被fgrep2.0收用。

BMH_AC算法

Wu-Manber算法在agrep中应用,并被glimpse收用。


原文链接:http://blog.csdn.net/ijuliet/article/details/4206487


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值