BM算法学习

 

     BM算法全称为Boyer-Moore算法,与KMP算法相似是一种精确地模式匹配算法。一般来说比KMP算法更加高效。

   

KMP算法比较

     相似点:都是寻找模式串自身的规律,在匹配失效时获得最大的跳转距离;

     不同点:实际操作中KMP算法是从左到右进行比较,而BM算法是从右到左。这就决定了在寻找规律的过程中,KMP算法的核心是寻找子串的相同前缀,而BM算法是寻找相同的后缀。

        KMP算法的核心请参见之前的博文《KMP算法学习》:http://blog.csdn.net/myjoying/article/details/7947119

        BM算法的核心是两条启发式规则:坏字符规则和好后缀规则。

 

坏字符规则

       所谓坏字符就是在比较过程中发现的第一个不匹配的字符。如下图中的红色字符。

        T a  b  c d  a  b  c  a  b

        P   d  a  b c a  b

 

      坏字符规则:在任意字符x处发生失配时,在模式串P中找到与Tx左最近的x字符位置。并将找到的x移动到与Tx对齐。

       

      情况1P中存在这样x,则将两个x对齐,重新比较;

         BM算法学习_第1张图片

      情况2P中不存在这样的x,则将模式串P整个移动到Tx位置之后的位置对齐。

 BM算法学习_第2张图片

      

好后缀规则

       所谓好后缀就是在一次比较过程中模式串与目标串相匹配的后缀。如下图中的绿色字符串。

         T a  b  c  a  b  c  a  b

         P  d  a  b  a  b

        好后缀规则:如果程序匹配了一个好后缀,并且在模式中还有另外一个相同的后缀,那把下一个后缀移动到当前后缀位置。

        

        情况1:模式串中存在与好后缀完全匹配的子串,则将最靠右的那个子串移动到与T中好后缀对应位置,继续比较。

          BM算法学习_第3张图片

 

        情况2:在模式串中不存在与好后缀完全匹配的好后缀,则在好后缀子串中寻找与模式串前缀完全匹配的最长后缀。

          BM算法学习_第4张图片

移动距离

      BM算法存在两条移动规则,包括坏字符和好后缀规则。在每次失配时模式串移动的距离取两者最大距离移动。

 

 

代码分析(转)

 

BM算法子串比较失配时,按坏字符算法计算模式串需要向右移动的距离,要借助BmBc数组。

注意BmBc数组的下标是字符,而不是数字

BmBc数组的定义,分两种情况。

1、 字符在模式串中有出现。如下图,BmBc[‘k’]表示字符k在模式串中最后一次出现的位置,距离模式串串尾的长度。

2、 字符在模式串中没有出现:,如模式串中没有字符p,则BmBc[‘p’] = strlen(模式串)。

wps_clip_image-1885

BM算法子串比较失配时,按好后缀算法计算模式串需要向右移动的距离,要借助BmGs数组。

BmGs数组的下标是数字,表示字符在模式串中位置。

BmGs数组的定义,分三种情况。

1、 对应好后缀算法case1:如下图:i是好后缀之前的那个位置。

BM算法学习_第5张图片

2、 对应好后缀算法case2:如下图所示:

BM算法学习_第6张图片

3、 当都不匹配时,BmGs[i] = strlen(模式串)

BM算法学习_第7张图片

在计算BmGc数组时,为提高效率,先计算辅助数组Suff。

Suff数组的定义:suff[i] = 以i为边界, 与模式串后缀匹配的最大长度,即P[i-s...i]=P[m-s…m]如下图:

BM算法学习_第8张图片

 

在计算BmGc数组时,为提高效率,先计算辅助数组Suff

Suff数组的定义:suff[i] =i为边界,与模式串后缀匹配的最大长度,即P[i-s...i]=P[m-sm]如下图:

 

Suff[]计算BmGs的方法:

1 BmGs[0m-1] = m;(第三种情况)

2)计算第二种情况下的BmGs[]值:

for (i = m - 1; i >= 0; --i)  
		
	if (suff[i] == i + 1)  
			
		for (; j < m - 1 - i; ++j)  
				
			if (bmGs[j] == m)  
					
				bmGs[j] = m - 1 - i; 

 

3)计算第一种情况下BmGs[]值,可以覆盖前两种情况下的BmGs[]值:

 

for (i = 0; i <= m - 2; ++i)  
					
	bmGs[m - 1 - suff[i]] = m - 1 - i;


 

 

如下图所示:

BM算法学习_第9张图片

Suff[]数组的计算方法。

常规的方法:如下,很裸很暴力。

Suff[m-1]=m;
for(i=m-2;i>=0;--i){
   q=i;
   while(q>=0&&P[q]==P[m-1-i+q])
   --q;
  Suff[i]=i-q;
}

 

有聪明人想出一种方法,对常规方法进行改进。基本的扫描都是从右向左。改进的地方就是利用了已经计算得到的suff[]值,计算现在正在计算的suff[]值。

如下图所示:

i 是当前正准备计算的suff[]值得那个位置。

f 是上一个成功进行匹配的起始位置(不是每个位置都能进行成功匹配的,  实际上能够进行成功匹配的位置并不多)。

q 是上一次进行成功匹配的失配位置。

如果i在q和f之间,那么一定有P[i]=P[m-1-f+i];并且如果suff[m-1-f+i]=i-q, suff[i]和suff[m-1-f+i]就没有直接关系了。

BM算法学习_第10张图片

 

 

#include <stdio.h>

#define ASIZE 26
#define XSIZE 6

//坏字符规则计算
void preBmBc(char *x, int m, int bmBc[]) {  
	int i;  
	for (i = 0; i < ASIZE; ++i)         
		bmBc[i] = m;  
	
	for (i = 0; i < m - 1; ++i)    
		bmBc[x[i]-'a'] = m - i - 1;    
} 
//stuff辅助数组计算   
void suffixes(char *x, int m, int *suff) 
{    
	int f, g, i;   
	f = 0;            //上次匹配成功的位置
	suff[m - 1] = m;   
	g = m - 1;        //上次匹配失败的位置
	for (i = m - 2; i >= 0; --i) {  
		
		if (i > g && suff[i + m - 1 - f] < i - g)  
			
			suff[i] = suff[i + m - 1 - f];    
		else {  
			
			if (i < g)  
				
				g = i;  
			
			f = i;  
			
			while (g >= 0 && x[g] == x[g + m - 1 - f]) //匹配成功则一直匹配 
				
				--g;  
			
			suff[i] = f - g;  
			
		}  
		
	}  
	
}  
//好后缀规则实现
void preBmGs(char *x, int m, int bmGs[]) {  
	
	int i, j, suff[XSIZE];  
	
	suffixes(x, m, suff);    
	for (i = 0; i < m; ++i)  //情况3
        bmGs[i] = m;  
	
	j = 0;  
	
	for (i = m - 1; i >= 0; --i)   //情况2 
		
		if (suff[i] == i + 1)  
			
			for (; j < m - 1 - i; ++j)  
				
				if (bmGs[j] == m)  
					
					bmGs[j] = m - 1 - i;  
				
	for (i = 0; i <= m - 2; ++i)    //情况1  
					
		bmGs[m - 1 - suff[i]] = m - 1 - i;  
				
}  

int MAX(int a, int b)
{
	return a>b? a: b;
}

void print(int *t, int m)
{
	for (int i=0; i<m; i++)
	{
		printf("%d ", t[i]);
	}
	printf("\n");
}
//BM算法主体部分,x为模式串,y为目标串
void BM(char *x, int m, char *y, int n) {  
	
	int i, j, bmGs[XSIZE], bmBc[ASIZE];  
	
	/* Preprocessing */  
	  
	
	preBmBc(x, m, bmBc);  
	print(bmBc, ASIZE);

	preBmGs(x, m, bmGs);
	print(bmGs, XSIZE);
	
	/* Searching */  
	
	j = 0;  
	
	while (j <= n - m) {  
		
		for (i = m - 1; i >= 0 && x[i] == y[i + j]; --i);  
		
		if (i < 0) {  
			
			printf("%d ",j);
			
			j += bmGs[0];  
			
		}  
		
		else  
			
			j += MAX(bmGs[i], bmBc[y[i + j]-'a'] - m + 1 + i);  
		
	}   
}  

int main()
{
	char target[11] = "iabcdabcab";
	char pattern[XSIZE+1] = "dabcab";

	BM(pattern, XSIZE, target, 10);

	return 0;
}


 

 

 

 

 

你可能感兴趣的:(BM算法学习)