BM算法根據兩個判據來進行字符串匹配,分別是“壞字符規則”和‘好后綴規則",其中好后綴規則可以單獨使用,算法的圖解可以參照下面這篇博文:
https://www.cnblogs.com/wxgblogs/p/5701101.html
采用Python語言對BM算法進行實現,實現過程分為3個函數,主循環函數和兩個判據的數組生成函數。
1 def my_BM(t,p): 2 '''bm算法的自我實現,在t串中匹配p串,從模式串的尾部開始匹配'''
3 '''需要壞字符數組badchar[]和好后綴數組goodsuffix[] 4 每次失配后,根據兩判據中最大的值移動p串,比較指針移動至最后'''
5 BadChar=BClist(p) 6 GoodSuffix=GSlist(p) 7 tlen,plen=len(t),len(p) 8 if tlen<plen: 9 return -1
10 i,k=plen-1,plen-1 #從p串尾部開始比較
11 move=0 12 while i<tlen and k>=0: 13 if t[i]==p[k]: 14 i,k=i-1,k-1
15 else: 16 BCmove=k-BadChar[ord(t[i])] if BadChar[ord(t[i])]!=-1 else plen 17 move=max(GoodSuffix[k],BCmove) #滑動位數
18 i,k=i+move+plen-1-k,plen-1
19 if k<=0: 20 return i+1
21 return -1
22
23
24 def BClist(p): 25 '''產生壞字符的失配移動表 26 j處失配,p串整體右移j-bc[T[j]]位'''
27 bc=[-1]*128 #標准ACCII表,可顯示128個常用字符
28 plen=len(p) 29 for i in range(plen): 30 bc[ord(p[i])]=i #利用ord--chr函數的互相轉化,間接直接將字符作為下標
31 return bc 32
33 def GSlist(p): 34 '''產生好后綴方法的失配移動表,若i處失配,則分三種:相應地跳過字符個數逐漸變大 35 1. 已匹配成功的字符串形成的后綴gs,在p串中x存在相等的子串,則將p串右移i-x+1; 36 2. 條件1不成立,則在p的前綴中尋找gs的后綴相等的最大串,設后綴頭在j,這一步類似於kmp的尋找最大相等前后綴,將p串右移j位; 37 3. 條件1,2都不成立,本輪比較失敗,將p串整體移動p長度m'''
38 plen=len(p) 39 GS=[plen]*plen #初始化數組,並直接置於條件3的值
40 GS[plen-1]=1 #好后綴規則可以單獨使用,它是基於已匹配的字符進行優化跳過,若首次匹配就失敗,則應該只移動p串一位
41 for i in range(plen-1): #i處失配
42 #條件2,求i之后的后綴串,其在p串前綴中的最大相等前綴
43 #雖然也是求最大相等前后綴,但與kmp不同,kmp求的是前綴子串中的最大相等前后綴,而好后綴算法中求的是整個p串的最大相等前后綴,只是對該前后綴的長度做了限制
44 k=0 45 j=i+1
46 while j<plen: 47 if p[k]==p[j]: 48 k,j=k+1,j+1
49 else: 50 j=j-k+1
51 k=0 52 if k!=0: 53 GS[i]=plen-1-k 54 #搜尋p串中是否還有與p[i+1:]相等的子串,即條件1
55 substr=find_last(p[:plen-1],p[i+1:]) #find_last(t,p)函數尋找t串中最后一個p串的起始位置,若沒有則返回-1 56 if substr!=-1: 57 GS[i]=i-substr+1
58 return GS