KMP算法python实现

目录

 

一、找出prefix_table

二、KMP算法实现

二、测试:

三、结果: 

四、时间复杂度简单匹配算法的时间复杂度为O(m*n),KMP匹配算法时间复杂度为O(m+n).。


一、找出prefix_table

1、把要匹配的字符串pattern拆成子串找出最大公共前后缀,原来的要匹配的字符串列入为求最大公共前后缀的行列里面。最大公共前后缀是指前后两个子串相同,而且子串的长度最大,例如子串ABABCABA的最大公共前后缀是ABA。计算出当前的最大公共前后缀的长度作为prefix_table的元素。前面加上-1,完全不匹配的时候把整个子串移动后一位,就相当于移动到-1的位置。

第3个子串ABAB的最大公共前后缀是AB,下一个子串只要判断下一个元素是不是A即可,假如是A,最大公共前后缀的长度+1,否则最大公共前后缀的长度-1,即此时要对比的对象是A,看看结尾的元素是不是A,是则把元素假如prefixtable。

def get_prefixtable(pattern):
    """
    通过传入要和text匹配的子字符串pattern,把pattern拆分成长度为1~len-1子串,
    分别找出子串的最大前后缀的长度组成prefix_table
    :param pattern:要匹配的子字符串
    :return:prefix列表
    """
    prefix = [None] * len(pattern)
    prefix[0] = 0 #第一个元素只有一个元素,没有前后缀所以为0
    length = 0 # 最大前后缀的长度
    i = 1 # 从第二个元素开始匹配
    while i < len(pattern):
        if pattern[i] == pattern[length]:
            length += 1
            # prefix[i] = length
            # i += 1
        else:
            # 主要是针对开头的元素,一直都匹配不到,
            # 当最大前后缀减到1时就会在开头元素不断循环,此时让他等于prefix[length - 1]即可
            if length > 0:
                length = prefix[length - 1]
            # else:
                # 记得一定要继续移动i指针
        prefix[i] = length
        i += 1
    return [-1] + prefix

二、KMP算法实现

pattern元素和text元素对相同则继续移动指针,假如不相同则查找prefixtable的值,该值对应的pattern元素是否相等,相等则继续匹配,否则重复上面操作,若是遇到-1的情况,则让i,j指针往后移动。

1、C和A不匹配,把子串整体往后移动,相当于对齐下标为-1的位置。

2、指针右移一位,ABAB都匹配上了,但是此时C不匹配,prefixtable显示2,把下标为2的地方对准当前指针位置,继续匹配,

此时匹配到字符串,返回当前指针坐标-子串长度,即可得到匹配到的起始位置。此时最后一位的prefixtable的值为3,把下标为3 的元素和当前指针指向元素对齐,发现要被匹配的子串已经超过了原字符串,所以不可能再会有匹配的位置。

def kmp(pattern, text):
    """
    pattern元素和text元素对相同则继续移动指针,假如不相同则查找prefixtable的值,
    该值对应的pattern元素是否相等,相等则继续匹配,否则重复上面操作,
    若是遇到-1的情况,则让i,j指针往后移动
    :param pattern: 要匹配的子串
    :param text: 原字符串
    :return: 匹配到的下标值的列表
    """
    i = 0
    j = 0
    result = []
    text_len = len(text)
    pattern_len = len(pattern)
    prefix = get_prefixtable(pattern)
    if text_len == 0:
        return -1
    while i < text_len:
        if text[i] == pattern[j]:
            i += 1
            j += 1
        else:
            j = prefix[j]
            # text不匹配的元素和prefix_table的头开始匹配的情况
            if j == -1:
                i += 1
                j += 1
        # 假如j == pattern_len - 1则已经匹配到子串最后一个元素,前面的元素都相等,
        # 判断假如text[i] == pattern[j]最后一个元素相等则返回开头的下标i-j当前text的位置减去匹配字符串的长度,
        # 为匹配的第一个元素位置
        if j == pattern_len - 1 and text[i] == pattern[j]:
            result.append(i - j)
            j = prefix[j]
    if len(result) == 0:
        return -1
    return result

二、测试:

if __name__ == '__main__':
    pattern = 'ABABCABAA'
    text = 'CABABABCABAABABA'
    text1 = 'ABABABCABAABABABCABAAABA'
    a = kmp(pattern, text)
    print(a)
    b = kmp(pattern, text1)
    print(b)
    print(kmp('ppppppp', text1))
    print(kmp('ABABCABAA', ''))

三、结果: 

[3] # 说明下标为3第一次匹配
[2, 12] # 说明下标为3第一次匹配 说明下标为12第二次匹配
-1 # 没找到
-1

四、时间复杂度简单匹配算法的时间复杂度为O(m*n),KMP匹配算法时间复杂度为O(m+n).。

发布了46 篇原创文章 · 获赞 75 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_38875300/article/details/89156184