python字符串匹配算法_字符串匹配算法之Kmp算法(Python实现)

Kmp算法的优势在于它只需要O(m)的与处理时间,而有限状态自动机最快也需要O(m * | Ʃ |)。Kmp算法的主要思路跟字符串自动机很像,在预处理阶段建立一个前缀函数,然后顺序扫描文本T,即可找出所有与模式P相匹配的字符串。前缀函数与字符串自动机中的转移函数功能相同,都是当遇到匹配失败时能根据前缀函数(或者转移函数),利用之前匹配的信息,能够找出下一个应该匹配的位置,避免类似朴素算法做过多的无用功。

图片来自《算法导论》。看图(a),文本T和模式P一直匹配成功前5个字符,但是第6个字符不匹配。但观察可以发现此时已经匹配的5个字符中的后三个是模式P的前三个字符,因此我们可以退而求其次地将已经匹配的字符数减少一点,看能否匹配新的字符。如图(b),此时比较新的字符是否匹配P的第四个字符,这样的比较实际上是把P左移了2位。这个2是这样得来的,原来已经匹配了5位,这5位的后缀中在P的最长前缀(“aba”)的长度是3,5-3=2由此得出应该左移2位。

代码实现

我们先假设已经能够得到前缀函数,即是说先不去管前缀函数是如何计算出的。那当我们已经有方法得到前缀函数后,如何匹配模式P?看下面这段python代码,注意为了方便理解,我在字符串T、P前面都加上了一个空格字符,效果是模拟字符串下标从1开始而不是从0开始。

def kmp_matcher(T, P):

T = ' ' + T

P = ' ' + P

n = len(T) - 1

m = len(P) - 1

t = KMP.longest_prefix_suffix(P)

q = 0

for i in range(1, n+1):

while q > 0 and P[q+1] != T[i]:

q = t[q]

if P[q+1] == T[i]:

q += 1

if q == m:

print i-m+1

q = 0

第6行调用函数longest_prefix_suffix,计算出模式P的前缀函数。第8行开始顺序扫描文本T,注意变量q记录此刻与模式P成功匹配的字符的个数。当下一个字符匹配失败时(P[q+1]!=T[i]),q的值根据前缀函数重新计算出,如9、10两行代码。当匹配成功时q的值只需简单加1。最后13行,当q的值(已经匹配的字符数)与模式P的长度相等时,我们便找到了一个匹配的字符串。

是时候来到最难理解的部分了(至少是我认为是最难理解的部分),计算前缀函数。其实如果不嫌慢的话可以暴力解法,但是时间复杂度是O(m^3),太差了。而书本给出的算法是O(m),对比产生美!

首先再说一下前缀函数的意思,前缀函数t[q]的物理意义是模式P的子串P[1..q]的后缀字符串中,是模式P的最大前缀的长度。

def longest_prefix_suffix(P):

if P[0] != ' ':

P = ' ' + P

m = len(P) - 1

t = [0] * (m+1)

k = 0

match = 0

for q in range(2, m+1):

while k > 0 and P[k+1] != P[q]:

k = t[k]

if P[k+1] == P[q]:

k += 1

t[q] = k

return t

一些理解

从代码上来看,计算前缀函数和匹配很相似,其实可以把计算前缀函数看作是和自己匹配的过程(书上这么说)。还是一样,为了数组下标从1开始,我把字符串下标0> 的位置放了一个空格。这段代码中变量k记录着当前匹配成功的字符的个数。11、12行代码是好理解的,当下一个字符匹配成功时,简单地把k加1。13行说的是,最后只需在下标为q的位置记录者子串P[1..q]的最长前缀数(k的值)。

对我来说,最难的部分在于理解9、10两行的代码,为什么当不匹配时只需不断的迭代(循环k = t[k]),便能找到适合的k值?

首先,发现对k不断地迭代(即k = t[k]),k的值会越来越小。回忆一下前缀函数的定义,t[q]表示P[1..q]的后缀,同时也是P的前缀的最大长度,所以其值显然要比q小。

所以有不等式:k > t[k] > t[t[k]] > ...

《算法导论》中有句话,通过对前缀函数不断进行迭代,就能列举出P[1..q]的真后缀中的所有前缀P[1..k]。如果真如其所说的话,那么9、10两行代码就好理解了,当匹配失败时,从大至小地列举出其所有前缀,找到一个能使下一个字符匹配成功的前缀即可。而从大到小地列举出所有前缀只需要循环地迭代其前缀函数即可。所以得知,(1)前缀函数不断迭代其值越来越小,(2)而且如书所说可以通过迭代来列举出所有可能的前缀。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值