后缀变前缀的KMP算法 2020-02-26(未经允许,禁止转载

2020-02-26  本文已影响0人  9_SooHyun

给定一个长度为n的主字符串str和一个长度为m的pattern(如str = 'qwertyuiopasdfghjkl',pattern = 'yuiop'),需要从str中找到被pattern命中的子串,你怎么做?

最简单的做法:BF算法

BF是bruteforce的缩写,暴力算法,事实上这根本不能称之为一个算法,基本思想就是【逐个检查字符是否匹配,不匹配则将pattern后移一个字符位置然后继续检查】。每次不匹配只后移动一个字符位置,做了很多无谓的比较,太慢了,时间复杂度O(n*m)

改进算法:KMP算法

先说结论,KMP算法相比BF算法的改进之处在于,每次移动都直接移动到下一个能够匹配pattern的起始字符的位置,【跳步移动】而不是一个一个字符地移动+【不总是从首个字符开始比较,减少了无谓的比较】。时间复杂度O(n+m)

1.KMP算法的基本思想——【后缀变前缀】:


主字符串"ababadababacambabacaddababacasdsd"(其中的斜体部分是匹配部分)
pattern串"ababaca"
为例

完整的用例过程如下:

2.利用next数组做到后缀变前缀

通过刚才的例子,pattern向右移动2个字符单位,可以使得pattern新的起始位置与suffix重合。那么这个2怎么得来的呢?
很简单

match_s = "ababa",
suffix = "aba",

len(match_s) - len(suffix) = steps => 5 - 3 = 2

因此,只需要得到match_s和最长前后缀就可以计算steps

一般地,使用一个长度为【m+1】的一维数组next存储match_s和最长前后缀信息,next的下标表示match_s的长度,存储的值表示最长(前)后缀的长度

对于pattern = "ababaca",match_s可以是'', 'a', 'ab', 'aba', 'abab', 'ababa', 'ababac', 共7个,因此对应next数组的长度为7
然后再把所有的match_s的最长(前)后缀的长度填入数组就大功告成
可以看到,next数组只与pattern有关,而与str无关

KMP算法之next数组

【重点】next数组的填充,可以通过动态规划实现

计算next数组的代码如下:

def getNextArray(pattern):
    # 创建next数组
    next_array = [0 for i in range(len(pattern))]
    prefix_len = 0
    # 开始填充next_array数组
    for i in range(2, len(pattern)):
        # 上一前缀长度
        prefix_len = next[prefix_len]
        # 加上prefix_len != 0是为了保证while能够正确结束,避免一直存在pattern[i] != pattern[prefix_len]产生死循环
        while pattern[i] != pattern[prefix_len] and prefix_len != 0:
            # 截断pattern,更新截断后上一前缀的长度
            prefix_len = next[prefix_len]
        if pattern[i] == pattern[prefix_len]:
            prefix_len += 1
        # 填充next_array[i]
        next_array[i] = prefix_len
上一篇下一篇

猜你喜欢

热点阅读