收藏 分销(赏)

BM模式匹配算法原理.doc

上传人:xrp****65 文档编号:7687641 上传时间:2025-01-12 格式:DOC 页数:13 大小:174.50KB 下载积分:10 金币
下载 相关 举报
BM模式匹配算法原理.doc_第1页
第1页 / 共13页
BM模式匹配算法原理.doc_第2页
第2页 / 共13页


点击查看更多>>
资源描述
BM模式匹配算法原理(图解) 修改浏览权限 | 删除 首先,先简单说明一下有关BM算法的一些基本概念。 BM算法是一种精确字符串匹配算法(区别于模糊匹配)。 BM算法采用从右向左比较 的方法,同时应用到了两种启发式规则,即坏字符规则 和好后缀规则 ,来决定向右跳跃的距离。 BM算法的基本流程: 设文本串T,模式串为P。首先将T与P进行左对齐,然后进行从右向左比较 ,如下图所示:     若是某趟比较不匹配时,BM算法就采用两条启发式规则,即坏字符规则 和好后缀规则 ,来计算模式串向右移动的距离,直到整个匹配过程的结束。        下面,来详细介绍一下坏字符规则 和好后缀规则 。      首先,诠释一下坏字符和好后缀的概念。    请看下图:      图中,第一个不匹配的字符(红色部分)为坏字符,已匹配部分(绿色)为好后缀。     1)坏字符规则(Bad Character):           在BM算法从右向左扫描的过程中,若发现某个字符x不匹配,则按如下两种情况讨论:                i. 如果字符x在模式P中没有出现,那么从字符x开始的m个文本显然不可能与P匹配成功,直接全部跳过该区域即可。                ii. 如果x在模式P中出现,则以该字符进行对齐。          用数学公式表示,设Skip(x)为P右移的距离,m为模式串P的长度,max(x)为字符x在P中最右位置。                       例1:          下图红色部分,发生了一次不匹配。                       计算移动距离Skip(c) = 5 - 3 = 2,则P向右移动2位。         移动后如下图:                         2)好后缀规则(Good Suffix):          若发现某个字符不匹配的同时,已有部分字符匹配成功,则按如下两种情况讨论:               i. 如果在P中位置t处已匹配部分P'在P中的某位置t'也出现,且位置t'的前一个字符与位置t的前一个字符不相同,则将P右移使t'对应t方才的所在的位置。               ii. 如果在P中任何位置已匹配部分P'都没有再出现,则找到与P'的后缀P''相同的P的最长前缀x,向右移动P,使x对应方才P''后缀所在的位置。          用数学公式表示,设Shift(j)为P右移的距离,m为模式串P的长度,j 为当前所匹配的字符位置,s为t'与t的距离(以上情况i)或者x与P''的距离(以上情况ii)。                    以上过程有点抽象,所以我们继续图解。          例2:           下图中,已匹配部分cab(绿色)在P中再没出现。                    再看下图,其后缀T'(蓝色)与P中前缀P'(红色)匹配,则将P'移动到T'的位置。                    移动后如下图:                      自此,两个规则讲解完毕。      在BM算法匹配的过程中,取SKip(x)与Shift(j)中的较大者作为跳跃的距离。      BM算法预处理时间复杂度为O(m+s),空间复杂度为O(s),s是与P, T相关的有限字符集长度,搜索阶段时间复杂度为O(m·n)。 最好情况下的时间复杂度为O(n/m),最坏情况下时间复杂度为O(m·n)。 (二) 所谓精确字符串匹配问题,是在文本 T 中找到所有与查询 P 精确匹配的子串。而 BM 算法可以非常有效地解决这个问题,让时间复杂度降到低于线形的水平。    BM 算法主要用了三种巧妙而有效的方法,即从右到左扫描,坏字符规则和好后缀规则。    从右到左扫描的意思是从最后一个字符开始向前匹配,而不是习惯上的从开头向后匹配。    坏字符规则是,从右到左的扫描过程中,发现 Ti 与 Pj 不同,如果P 中存在一个字符 Pk 与 Ti 相同,且 k<i 那么就将直接将 P 向右移使 Pk 与 Ti 对齐,然后再从右到左进行匹配。如果 P 中不存在任何与 Ti 相同的字符,则直接将 P 的第一个字符与 Ti 的下一个字符对齐,再从右到左进行比较。    如图:    T:     a b c b a d f t a t e    P:     c b a x a d    P:         c b a x a d       用 R(x) 表示字符 x 在 P 中出现的最右位置,此例中 R(b)=2。    可以看出使用从右到左扫描和坏字符规则可以跳过 T 中的很多位置不去检查,从而使时间复杂度低于线性。    好后缀规则是,从右到左的扫描过程中,发现 Ti 与 Pj 不同,检查一下相同的部分 t 是否在 P 中的其他位置 t' 出现,a) 如果 t 与 t' 的前一个字母不相同,就将 P 向右移,使 t' 与 T 中的 t 对齐。b) 如果 t' 没有出现,则找到与 t 的后缀相同的 P 的最长前缀 x,向右移动P ,使 x 与 T 中 t 的后缀相对应。    如图a):    N:                      1                        N:    1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8         T:    a b c b a d f t b c f a q v t b c e...    P:    c b c a b c e a b c    P:                  c b c a b c e a b c f       可见,并不是将 P 向右移让 P5 与 T9 对齐,而是让 P2 与 T9 对齐,因为 P1 与 P8 不相同。用 L(i) 表示 t' 的最大位置,此例中, L(9)= 3。    如图b):    N:                      1                        N:    1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8         T:    a b c b a d f t b c f a q v t b c e...    P:    b c c a b c e t b c    P:                    b c c a b c e t b c                            可见,当 P 向左找不到 “tbc”时,就找到 “tbc”的最长与 P 的前缀匹配的后缀,并将 P 向右移。用 l(i) 表示这个最长后缀的长度,这个例子中 i=8。      整个算法是这样的: [预处理]    输入查询字符串 P,    计算 P 中每个位置的 L(i) 和 l(i),并计算 R(i)。 [查询]    k:=n; // n 是 T 中字符的总数    while k<=m do      begin      i :=n; // i 表示 P 中字符的位置      h :=k; // h 表示 T 中字符的位置      while i>0 and P(i)=T(i) do         begin           i:=i-1;           h:=h-1;        end;      if i=0 then        begin          输出 T 的这个位置上的字符串;          k:= k+n-l(2);        end      else         移动 P(增加 k),k 取 好后缀规则和坏字符规则决定的最大值      end;                预处理阶段可以根据上一篇文章提到的 Zbox 方法进行处理,时间复杂度为线性。        整个算法的时间复杂度最坏的情况是 O(m),m 是 T 的长度。 (三) BM算法的基本思想是从右向左进行比较。 开始时仍是P(Pattern)的最左边与S(String)的最左边对齐,但首先进行Pm与Tm的比较。 当某趟比较中出现不匹配时,BM算法采用两条启发性规则计算模式串右移的距离,即坏字符规则和好后缀规则。   1) 坏字符规则(Bad Character)      P中的某个字符与T中的某个字符不相同时使用坏字符规则右移模式串P,      P右移的距离可以通过delta1函数计算出来。delta1函数的定义如下:       delta1(x) = - m; x<>P[j] (1 <= j <= m),即x在P中未出现                   |                  - m - max{k|P[k] = x, 1 <= k <= m}; 其它情况   2) 好后缀规则(Good Suffix)      该规则将KMP算法和BM算法的思想结合起来,在不丢失真解的前提下确定一个新的移动距离delta2,      该函数与样本P有关。      P中的某一子串P[j-s+1..m-s]与已比较部分P[j+1..m]相同,可让P右移s位。      delta2的定义如下:      delta2(j)= {s|P[j+1..m]=P[j-s+1..m-s])&&(P[j]≠P[j-s])(j>s)}  在匹配过程中,取delta1和delta2中的大者。  BM算法的最坏时间复杂度为O(m*n),  但实际比较次数只有文本串长度的20%~30%。 ******************************************  BM字符串匹配算法  从snort2.2的mstring.c中整理而来   ****************************************** #include <stdio.h> #include <string.h> ******************************************     生成skip数组,即delta1数组   ****************************************** int *make_skip(char *ptrn, int plen) { int *skip = (int *)malloc(256 * sizeof(int)); int *sptr = skip + 256; if (skip == NULL)    fprintf(stderr, "malloc failed!");    while (sptr-- != skip)    *sptr = plen + 1;    while (plen != 0)    skip[(unsigned char)*ptrn++] = plen--;    return skip; } ******************************************   生成shift数组,即delta2数组 ****************************************** int *make_shift(char *ptrn, int plen) { int *shift = (int *)malloc(plen * sizeof(int)); int *sptr = shift + plen - 1; char *pptr = ptrn + plen - 1; char c; if (shift == NULL)    fprintf(stderr, "malloc failed!"); c = ptrn[plen - 1]; *sptr = 1; while (sptr -- != shift) {    char *p1 = ptrn + plen - 2, *p2, *p3;       do    {     while (p1 >= ptrn && *p1-- != c);         p2 = ptrn + plen - 2;     p3 = p1;         while (p3 >= ptrn && *p3-- == *p2-- && p2 >=pptr);    }while (p3 >= ptrn && p2 >= pptr);       *sptr = shift + plen - sptr + p2 - p3;       pptr--; } return shift; } ******************************************     搜索函数  **************************************** int mSearch(char *buf, int blen, char *ptrn, int plen, int *skip, int *shift) { int b_idx = plen; if (plen == 0)    return 1;    while (b_idx <= blen) {    int p_idx = plen, skip_stride, shift_stride;       while (buf[--b_idx] == ptrn[--p_idx])    {     if (b_idx < 0)      return 0;         if (p_idx == 0)     {      return 1;     }    }       skip_stride = skip[(unsigned char)buf[b_idx]];    shift_stride = shift[p_idx];       b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride; } return 0; }   int main() { char str[100] = "faecabcxxdefeabcxxxabcdwaw"; char pattern[10] = "abcxxxabc"; int *skip, *shift, i; skip = make_skip(pattern, strlen(pattern)); shift = make_shift(pattern, strlen(pattern)); if (!mSearch(str, strlen(str), pattern, strlen(pattern), skip, shift))    printf("The string \"%s\" doesn't contain string \"%s\"\n", str, pattern); else    printf("The string \"%s\" does contain string \"%s\"\n", str, pattern); return 0;
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 教育专区 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服