python--数据结构--KMP_字符串匹配

# kmp_str_match.py
from array import array
from collections import deque


def next_pattern(pattern):
    """
    计算模式串的next_数组

    next_数组下标:
        前缀的下一个位置

    next_数组元素:
        前缀的最长可匹配前缀的下一个位置

    主串:
        将模式串作为主串

    模式串:
        模式串本身

    算法思想:
            利用已计算出的next_数组中的元素计算剩余的next_数组中未计算出的元素,整体思想依然是KMP
        思想,在已匹配的字符串当中寻找到最长可匹配后缀子串和最长可匹配前缀子串,在下一轮直接把两者
        对齐,从而实现模式串的快速移动。

    该算法代码结构与KMP代码结构类似,只是具体分支操作不同

    :param pattern: 模式串
    :return: next_数组
    """
    pat_len = len(pattern)  # 模式串长度
    next_ = array('i', [0]*pat_len)

    # i的起始位置为2的原因:
    # i==0时,没有前缀,因此设置为0;
    # i==1时,有前缀,但前缀元素只有1个,该前缀没有最长可匹配前缀子串,因此设置为0
    i = 2  # 主串下标从2开始
    j = 0  # 模式串下标从0开始
    while i < pat_len:  # 遍历主串
        if pattern[i-1] == pattern[j]:
            # 字符匹配则更新i和j
            next_[i] = j + 1
            i += 1
            j += 1
        else:
            j = next_[j]  # pattern[i] != pattern[j]时对j进行回溯
            if j == 0 and pattern[0] != pattern[i-1]:
                # 避免j==0时出现死循环
                next_[i] = 0
                i += 1
    return next_


def kmp(target, pattern):
    """
    kmp字符串匹配

    算法思想:
            在已匹配的字符串当中寻找到最长可匹配后缀子串和最长可匹配前缀子串,在下一轮直
        接把两者对齐,从而实现模式串的快速移动。

    具体的解析:
            可参考https://baijiahao.baidu.com/s?id=1659735837100760934&wfr=spider&for=pc,
        但获取next数组部分解析最好不要参考其,不易理解,应把模式串看成两个不同的串,主串和模
        式串。

    时间复杂度:
        O(len(target)+len(pattern))
	
	空间复杂度:
        O(len(pattern)),空间next数组的大小是len(pattern)

    :param target: 主串
    :param pattern: 模式串
    :return: 模式串在主串中的位置
    """
    next_ = next_pattern(pattern)  # 获取模式串的next_数组
    i = 0  # 主串下标从0开始
    j = 0  # 模式串下标从0开始
    pat_len = len(pattern)  # 模式串的长度
    tar_len = len(target)  # 主串的长度
    pos = deque()  # 保存模式串在主串中的多个匹配位置
    while i < tar_len:  # 遍历主串
        if target[i] == pattern[j]:
            # 字符匹配则更新i和j
            i += 1
            j += 1

            if j == pat_len:
                # 在主串中成功匹配到模式串
                pos.append(i - pat_len)  # 获得模式串在主串中的多个匹配位置
                j = 0
        else:
            j = next_[j]  # target[i] != pattern[j]时对j进行回溯
            if j == 0 and target[i] != pattern[j]:
                # 避免j==0时出现死循环
                i += 1
    return pos


if __name__ == '__main__':
    target = 'abc;GTGTG566464kjlhlGTGTGSFDWD'
    pattern = 'GTGTG'
    pos = kmp(target, pattern)
    print(pos)


"""
运行结果:
deque([4, 20])

Process finished with exit code 0
"""

你可能感兴趣的:(python,#,数据结构--python,数据结构,python,KMP)