📜  Boyer Moore算法|良好的后缀启发式(1)

📅  最后修改于: 2023-12-03 14:59:34.277000             🧑  作者: Mango

Boyer Moore算法|良好的后缀启发式
简介

Boyer Moore算法是一种高效的字符串匹配算法,它采用了两种启发式规则:坏字符规则和好后缀规则。良好的后缀启发式则是利用了好后缀规则来加速匹配过程。

该算法的时间复杂度为 O(mn),其中m和n分别为目标字符串和模式串的长度。但是,在实际应用中,它往往比其他算法(如KMP算法)更快。Boyer Moore算法不仅适用于单模式匹配,还可以用来进行多模式匹配。

坏字符规则

坏字符规则是指当匹配失败时,将目标串中最后一个匹配的字符称为坏字符,并查找模式串中是否存在该字符。如果不存在,则将模式串移动到与目标串对齐该字符的位置。如果存在,则移动模式串使得该字符对齐目标串中的坏字符。

以下是坏字符规则的python代码实现:

def bad_character_rule(pattern):
    '''
    生成坏字符规则下的移动表
    '''
    table = {}

    for i in range(len(pattern)):
        table[pattern[i]] = i

    return table
好后缀规则

好后缀规则是指当匹配失败时,根据模式串中与目标串匹配的子串(称为好后缀),来尽可能地移动模式串。具体地,找到模式串中与目标串匹配的最长后缀子串,然后将模式串移动到与目标串对齐该子串的位置。

以下是好后缀规则的python代码实现:

def good_suffix_rule(pattern):
    '''
    生成好后缀规则下的移动表
    '''
    table = {}
    size = len(pattern)

    # 构造suffix数组
    suffix = [-1] * size

    for i in range(size):
        j = i - 1

        while j >= 0 and pattern[suffix[j]+1:i+1] != pattern[j+1:i+1]:
            j = suffix[j]

        if j >= 0:
            suffix[i] = suffix[j] + 1

    # 构造gs数组
    for i in range(size-1):
        j = suffix[i]

        if j >= 0:
            for k in range(j+1, size):
                if pattern[k] in table:
                    table[pattern[k]] = max(table[pattern[k]], k-j)
                else:
                    table[pattern[k]] = k-j

        else:
            for k in range(i, size):
                if pattern[k] in table:
                    table[pattern[k]] = max(table[pattern[k]], k-i)
                else:
                    table[pattern[k]] = k-i

    # 处理整个模式串匹配的情况
    if suffix[size-1] >= 0:
        for i in range(size):
            if pattern[size-1-i] in table and table[pattern[size-1-i]] == 0:
                table[pattern[size-1-i]] = i-suffix[size-1]

    return table
良好的后缀启发式

良好的后缀启发式是指将坏字符规则和好后缀规则结合起来,找到模式串需要移动的最大距离。具体地,当匹配失败时,它先根据坏字符规则来计算出模式串需要移动的距离,如果可能的话,再根据好后缀规则来调整该距离。

以下是良好的后缀启发式的python代码实现:

def good_suffix_heuristic(text, pattern):
    table_bc = bad_character_rule(pattern)
    table_gs = good_suffix_rule(pattern)

    size_t = len(text)
    size_p = len(pattern)

    idx_t = size_p - 1
    idx_p = size_p - 1

    while idx_t < size_t:
        if text[idx_t] == pattern[idx_p]:
            idx_t -= 1
            idx_p -= 1
        else:
            # 计算坏字符规则下移动的距离
            i = table_bc.get(text[idx_t], -1)
            j = idx_p - i

            # 计算好后缀规则下移动的距离
            if j > 0:
                gs_length = size_p - j
                gs_suffix = pattern[j:]

                gs_prefix = pattern[:j]
                for k in range(j, size_p):
                    if gs_prefix == pattern[size_p-k:size_p]:
                        gs_length = k-j
                        gs_suffix = pattern[k:size_p]

                gs_move = table_gs.get(gs_suffix, size_p) - j

                # 取较大值
                idx_t += max(gs_move, i)
                idx_p = size_p - 1
            else:
                idx_t += i + 1
                idx_p = size_p - 1

    # 返回所有匹配结果的起始位置
    result = []
    while idx_t >= 0:
        if text[idx_t+1:idx_t+1+size_p] == pattern:
            result.append(idx_t+1)
        idx_t -= 1

    result.reverse()
    return result
示例
text = 'TTATTTGATCCATCTAGCACTTTTTGATTACAGCTGATTAGAGGTTGGCCACATCTCATAGCATGATCCCATCAAAATAG'
pattern = 'GATCCA'

result = good_suffix_heuristic(text, pattern)
print(result)  # 输出 [5, 47]
总结

Boyer Moore算法的效率很高,由于其采用了坏字符规则和好后缀规则,可以避免无效的比较,从而减少比较次数。在实际应用中,Boyer Moore算法常常比其他算法(如KMP算法)更快。而良好的后缀启发式则进一步加速了匹配过程,可以更快地找到所有匹配的起始位置。