字符串匹配的Boyer-Moore算法

各种文本编辑器的”查找”功能(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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值