前些日子分析了一下BM演算法,時間上斷斷續續的,效率很低,今天上午才算最後完工,關於BM演算法我主要參考了以下內容:
1.http://ouyangjia7.iteye.com/blog/353137
2.http://blog.csdn.net/chong232/archive/2010/08/12/5806968.aspx
這兩篇文章都寫得很好,給我有很多協助,但是看上面的原始碼有一些地方我自己不理解,所以按照自己的思想改動了一些地方,現在將原始碼貼下,我進行了簡單的測試,全部正確,不過肯定測試的不完善,大家如果發現了錯誤請留言, 我好改正,大家對代碼中有不明白的地方也可以留言,我可以幫忙說下,代碼如下:#include <stdio.h><br />#include <stdlib.h><br />#include <string.h></p><p>/*<br /> 函數:int* MakeSkip(char*,int)<br />母的:根據壞字元規則做預先處理,建立一張壞字元表<br /> 表的長度由字元的規模而定,<br /> 如果只有字母則長度只有26,<br /> 如果是字母加數字長度就是26+10<br />參數:<br /> ptrn=>模式串P<br /> pLen=>模式串P長度<br /> 返回:<br /> int* - 壞字元表<br />*/<br />int *MakeSkip(char* ptrn,int pLen)<br />{<br />int i;<br />int len=pLen;<br />char *p=ptrn;<br />//為建立壞字元表,申請256個int的空間<br />int *skip=(int*)malloc(256*sizeof(int));<br />if(skip==NULL)<br />{<br />printf("malloc failed!");<br />return 0;<br />}<br />//初始化壞字元表,256個單元全部初始化為pLen<br />for(i=0;i<256;i++)<br />{<br />*(skip+i)=pLen;<br />}<br />//賦值,從左至右遍曆ptrn,這樣如果一個字元出現兩次,後面的覆蓋前面的,<br />//不在模式中出現的字元不用再賦值,它們使用預設值pLen。<br />while(pLen!=0)<br />{<br />*(skip+(int)*ptrn++)=--pLen;<br />}<br />return skip;<br />}<br />/*<br />函數:int *MakeShift(char*,int)<br />目的:根據好尾碼原則做預先處理,建立一張好尾碼表<br />參數:<br />ptrn=>模式串P<br />pLen=>模式串P的長度<br />返回:<br />int* :好尾碼表<br />*/<br />int *MakeShift(char *ptrn,int pLen)<br />{<br />//為好尾碼表申請pLen個int的空間<br />//這樣,第一個位置放置長度為1的尾碼<br />int *shift=(int *)malloc(pLen*sizeof(int));<br />int *sptr=shift+pLen-1;//方便為好尾碼表進行賦值的指標<br />char *pptr=ptrn+pLen-1;//記錄好尾碼表邊界位置的指標<br />char c;<br />//int i;<br />if(shift==NULL)<br />{<br />fprintf(stderr,"malloc failed!");<br />return 0;<br />}<br />c=*(ptrn+pLen-1);//儲存模式串中最後一個字元,因為要反覆用到它<br />*sptr=1;//以最後一個字元為邊界時,移動距離設為1(因為要與壞字元規則比較,所以這個是個假設,1也是最小的移動距離)<br />pptr--;//邊界移動到倒數第二個字元<br />while(--sptr>=shift)//該最外層迴圈完成給好尾碼表中的每一個單元進行賦值的工作<br />{<br />char *p1=ptrn+pLen-2,*p2,*p3;<br />//該do...while迴圈完成以當前pptr所指向的字元為邊界時,要移動的距離<br />do<br />{<br />while(p1>=ptrn&&*p1--!=c);//該空迴圈,尋找與最後一個字元c匹配的字元所指向的位置<br />if(p1<ptrn)<br />{<br />*sptr=pLen;<br />break;<br />}<br />p2=ptrn+pLen-2;<br />p3=p1;<br />while(p3>=ptrn&&*p3--==*p2--&&p2>=pptr);//該空迴圈,判斷在邊界內字串匹配到什麼位置<br />if(p2<pptr)<br />{<br />if(*++p3==*++p2)<br />{<br />*sptr=p2-p3;<br />break;<br />}<br />if(*p2==*p3)<br />{<br />continue;<br />}<br />}<br />if(p3<ptrn)<br />{<br />*sptr=p2-p3;<br />break;<br />}<br />}while(p3>=ptrn);<br />pptr--;//邊界繼續向前移動<br />}<br />return shift;<br />}<br />/*<br />函數:int* BMSearch(char*,int ,char*,int,int*,int*)<br />目的:判斷文本串是否包含模式串P<br />參數:<br />buf->文本串T<br />blen->文本串T長度<br />ptrn->模式串P長度<br />plen->模式串P長度<br />skip->壞字元表<br />shift->好尾碼表<br />返回:<br />int->1表示成功(文本串包含模式串),0表示失敗(文本串不包含模式串)<br />*/<br />int BMSearch(char *buf,int blen,char *ptrn,int plen,int *skip,int *shift)<br />{<br />int b_idx=plen;<br />if(plen==0)<br />{<br />return 1;<br />}<br />while(b_idx<=blen)//計算字串是否匹配到了盡頭<br />{<br />int p_idx=plen,skip_stride,shift_stride;<br />int i=0;<br />int temp=b_idx;//是為了不改動b_idx的值,b_idx將來用於計算移動的距離<br />while(buf[--temp]==ptrn[--p_idx])//開始匹配<br />{<br />i++;<br />if(p_idx==0)<br />{<br />fprintf(stderr,"match at %d!",b_idx);<br />return 1;<br />}<br />}<br />printf("i:%d/tbad:%c/n",i,buf[temp]);<br />skip_stride=skip[(unsigned char)buf[temp]]-i;//根據壞字元規則計算跳躍的距離<br />shift_stride=shift[p_idx];<br />printf("b_idx:%d 1:%d 2:%d/n",b_idx,skip_stride,shift_stride);<br />b_idx+=(skip_stride>shift_stride)?skip_stride:shift_stride;//取最大者<br />}<br />return 0;<br />}<br />int main()<br />{<br />char *buf="bcczcsabcd";<br />char *ptrn="abc";<br />int *skip=NULL;<br />int *shift=NULL;</p><p>fprintf(stderr,"plen=%d!/n",strlen(ptrn));</p><p>skip=MakeSkip(ptrn,strlen(ptrn));<br />shift=MakeShift(ptrn,strlen(ptrn));<br />BMSearch(buf,strlen(buf),ptrn,strlen(ptrn),skip,shift);<br />return 1;<br />}