各种文本编辑器的”查找”功能(Ctrl+F),大多采用Boyer-Moore算法。
bm算法的巧妙之处就是他跳过很多不必要的字符,一般只会匹配20-30%的字符串就行了。
查找过程是:
1、首先,"字符串"与"搜索词"头部对齐,从尾部开始比较,如果匹配就一直向前查找,如果整个都匹配那就找到了要查找的字符串
2、如果有不匹配的,就将字符串想后移动
1)如果是第一个就不匹配,且不匹配的字符不在要查找的字符串中,那就将整个字符串后移
如果在,移到第一次出现字符的地方(倒数)
例如:"iloveyou"中查找“need”,v 不在need中,就直接后移4个字符,变为 eyou与need做匹配
iloveyou中查找video,e与o不匹配但是在video中,移动到e位置(后移一位),也就是变成lovey与video匹配
2)如果不是第一个不匹配,也就是说有几个字符已经匹配了,别人又称“好后缀”,那么把这个后缀看成一个整体,
按照1)中的处理,如果这个后缀在要查找的字符串理中有就移到倒数第一个(不包含当前的)那里对齐,重新开始匹配
如果没有就直接将匹配的头与下一个字符对齐,重新开始匹配
filenameisreadme,中查找yourname,显然有好后缀name,但是下一个字符e与r不匹配,且name在yourname中出后最后面没有了,就直接后移
变成isreadme与yourname匹配
filenameisreadme,中查找namename,会变成nameisreadme与namename匹配
其实2)的情况里面还有遇到不匹配的字符,也符合1)中的不匹配逻辑的
移动的距离就是1)和2)中的最大值
如何快速的取得后移的位数呢?这就需要生成几个中间数据,是的移动位数的获得是O(1)的
1、BmBc,坏字符处理,记录每个字符要移动的位置,parten_len为要查找的字符串的长度,self.parten为要查找的字符串
要移动的距离,显然是本字符到字符串末尾的距离,例如在好后缀中,可能出现通过这个得到位置已经搜索过了,这个不要紧,此时的取最大值会用好后缀得到的数
def preBmBc(self):
print "preBmBc "
parten_len = self.parten_len
for i in xrange(parten_len):
self.bmBc[self.parten[i]] = parten_len -i -1
2)好字符,这个要复杂些(这些描述,这篇文章的图很好http://blog.csdn.net/appleprince88/article/details/11881323)
为了实现好后缀规则,需要定义一个数组suffix[],其中suffix[i] = s 表示以i为边界,与模式串后缀匹配的最大长度,如下图所示,用公式可以描述:满足P[i-s, i] == P[m-s, m]的最大长度s。
临时数据时找到所有的后缀字段,key为位置,value为从此字符往前的最大后缀,比如最后一个字符,显然为整个字符串,第一个c前面的abc为后缀,值也就是3
abcsdabc得到的dict为
{0: 0, 1: 0, 2: 3, 3: 0, 4: 0, 5: 0, 6: 0, 7: 8}
def preSuffix(self):
print "preSuffix"
m = self.parten_len
self.suffix[m - 1] = m
for i in xrange(m - 2,-1,-1):
q = i
while q>=0 and self.parten[q] == self.parten[m - i -1 + q]:
q-=1
self.suffix[i] = i - q
下面的代码来生成好后缀的移动距离
构建bmGs数组分为三种情况,分别对应上述的移动模式串的三种情况
模式串中没有子串匹配上好后缀,但找不到一个最大前缀
模式串中有子串匹配上好后缀
模式串中没有子串匹配上好后缀,但找到一个最大前缀
def preBmGs(self):
print "preBmGs"
m = self.parten_len
j = 0
#模式串中没有子串匹配上好后缀,但找到一个最大前缀
for i in xrange(m - 1,-1,-1):
if self.suffix[i] == i + 1:
for k in xrange(j,m-1-i):
if self.bmGs.get(k,None) is None:
self.bmGs[k] = m-1-i
j = k
#其实这个也可以,可以替换上面的,这个更好理解些
# for i in xrange(0,m):
# if self.suffix[i] == i + 1:
# for k in xrange(i+1,m-1-i):
# if self.bmGs.get(k,None) is None:
# self.bmGs[k] = m-1-i
#模式串中有子串匹配上好后缀
for i in xrange(0,m-1):
self.bmGs[m - 1 - self.suffix[i]] = m - 1 - i;
最后的搜索
def search(self):
'''
j = 0;
while (j <= strlen(T) - strlen(P)) {
for (i = strlen(P) - 1; i >= 0 && P[i] ==T[i + j]; --i)
if (i < 0)
match;
else
j += max(bmGs[i], bmBc[T[i]]-(m-1-i));
}
'''
j = 0
m = self.parten_len
lent = len(self.text)
lenp = self.parten_len
if lent < lenp:
return -1
while j <= lent - lenp:
for i in xrange(lenp - 1,-1,-1):
if self.parten[i] == self.text[i+j]:
if i <= 0:
return j
else:
j += max(self.bmGs[i],self.bmBc.get(self.text[i],lenp) -(m-1-i))
break
return -1
完整代码
#coding=gbk
class BM(object):
def __init__(self, text, parten):
self.text = text
self.parten = parten
self.parten_len = len(parten)
self.bmBc = {}
self.suffix = {}
self.bmGs = {}
self.pre_parten()
def pre_parten(self):
self.preBmBc()
self.preSuffix()
self.preBmGs()
print "after pre"
def preBmBc(self):
print "preBmBc "
parten_len = self.parten_len
for i in xrange(parten_len):
self.bmBc[self.parten[i]] = parten_len -i -1
def preSuffix(self):
print "preSuffix"
m = self.parten_len
self.suffix[m - 1] = m
for i in xrange(m - 2,-1,-1):
q = i
while q>=0 and self.parten[q] == self.parten[m - i -1 + q]:
q-=1
self.suffix[i] = i - q
print self.suffix
def preBmGs(self):
print "preBmGs"
m = self.parten_len
j = 0
#模式串中没有子串匹配上好后缀,但找到一个最大前缀
for i in xrange(m - 1,-1,-1):
if self.suffix[i] == i + 1:
for k in xrange(j,m-1-i):
if self.bmGs.get(k,None) is None:
self.bmGs[k] = m-1-i
j = k
#其实这个也可以,可以替换上面的,这个更好理解些
# for i in xrange(0,m):
# if self.suffix[i] == i + 1:
# for k in xrange(i+1,m-1-i):
# if self.bmGs.get(k,None) is None:
# self.bmGs[k] = m-1-i
#模式串中有子串匹配上好后缀
for i in xrange(0,m-1):
self.bmGs[m - 1 - self.suffix[i]] = m - 1 - i;
def search(self):
'''
j = 0;
while (j <= strlen(T) - strlen(P)) {
for (i = strlen(P) - 1; i >= 0 && P[i] ==T[i + j]; --i)
if (i < 0)
match;
else
j += max(bmGs[i], bmBc[T[i]]-(m-1-i));
}
'''
j = 0
m = self.parten_len
lent = len(self.text)
lenp = self.parten_len
if lent < lenp:
return -1
while j <= lent - lenp:
for i in xrange(lenp - 1,-1,-1):
if self.parten[i] == self.text[i+j]:
if i <= 0:
return j
else:
print i
j += max(self.bmGs[i],self.bmBc.get(self.text[i],lenp) -(m-1-i))
break
return -1
if __name__ == '__main__':
bm = BM("i am a axample,to search some string,add more example","example")
print bm.search()
参考文献:
http://blog.csdn.net/appleprince88/article/details/11881323