6.算法进阶——字符串匹配问题——BM算法

在这里插入图片描述
最简单的暴力方法大家应该都会写,我们来看一下优化。
最主要的问题在于,能不呢利用之前已经匹配的一部分,进行继续匹配,而不是从头开始

BM算法

基于预处理来避免不必要的重复。从模式串的末尾,往前匹配
在这里插入图片描述

坏字符规则:发现主串坏字符,检查模式串中是否存在坏字符,不存在直接跳过

坏字符的作用是跳过一些肯定不可能成立的匹配位置,描述的是主串上的失配字符。
在这里插入图片描述
当我们对其s和p之后,从p的末尾即p[6]开始匹配。发现和s[6]不一致,此时s中不匹配的s[6]=S,就是坏字符

发现坏字符之后。检查模式串p中是否有S,没有,直接从S[7]开始匹配

因为没有就说明了,模式串中任何一个部分都不可能与目前的s重叠,跳过这一段,从主串的下一位置开始匹配
在这里插入图片描述
此时同样P E两个字符不匹配,检查模式串中是否存在坏字符P。有
此时将模式串最后一次出现的P,与s对齐

在这里插入图片描述

注意,这里最后一次出现的位置可能比坏字符位置大,导致实际上模式串往后移动了负数位,即往前移动了
所以模式串移动的位数应该和1取max,以保证至少移动一位

好后缀规则:

我们注意到,SIMPLE 和 EXAMPLE 的匹配中,我们发现“MPLE”都匹配上。
直到主串的I和模式串的A,我们称“MPLE”为一个好后缀。

根据坏字符规则,此时坏字符I在模式串中不存在,应该移动到I的后一位。即 A在模式串的下标2-(I在模式串中不存在为-1)=3位。
好后缀规则,就是想让其多移动几位。

我们拿出好后缀,看其字串“PLE”、“LE”、“E”是否出现在模式串EX中。
这里E出现了,于是我们把模式串的E和主串的E对齐。
在这里插入图片描述

代码实现

坏字符最右位置计算:


def get_bc(pattern):
    bc = dict() # 记录每个badchar最右出现的位置
    for i in range(len(pattern) - 1):
        char = pattern[i]
        bc[char] = i + 1
    return bc

好后缀偏移表计算:


def get_gs(pattern):
    gs = dict()
    gs[''] = len(pattern)

    # suf_len 用于标记后缀长度
    for suf_len in range(len(pattern)):
        suffix = pattern[len(pattern) - suf_len - 1:]
        # j 用于标记可用于匹配的位置
        for j in range(len(pattern) - suf_len - 1):
            substr = pattern[j:j + suf_len + 1]
            if suffix == substr:
                gs[suffix] = len(pattern) - j - suf_len - 1

    for suf_len in range(len(pattern)):
        suffix = pattern[len(pattern) - suf_len - 1:]
        if suffix in gs: continue
        gs[suffix] = gs[suffix[1:]]

    gs[''] = 0
    return gs

匹配过程:


def bm(string, pattern, bc, gs):
    # i 用于标记当前模式串和主串哪个位置左对齐。
    i = 0 
    # j 用于标记当前模式串匹配到哪个位置;从右往左遍历匹配。
    j = len(pattern)

    while i < len(string) - len(pattern) and (j > 0):
            # 从右往左匹配每个位置
            a = string[i + j - 1]
            b = pattern[j - 1]
            if a == b: # 匹配的上,继续匹配前一位
                j = j - 1
            else: # 匹配不上,根据两个规则的预处理结果进行快速移动
                i = i + max(gs.setdefault(pattern[j:], len(pattern)), j - bc.setdefault(a, 0))
                j = len(pattern)
            # 匹配成功返回匹配位置
            if j == 0:
                return i
    # 匹配失败返回 None
    return -1
    
if __name__ == '__main__':
    string = 'here is a simple example ' 
    pattern = 'example'

    bc = get_bc(pattern)  # 坏字符表
    gs = get_gs(pattern)  # 好后缀表

    print(gs)

    x = bm(string, pattern, bc, gs)

    print(x)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值