模式匹配003: KMP

1. 概述

KMP算法是Knuth、Morris、Pratt在1977年提出的一种基于前缀的匹配算法。该算法通过预处理模式计算得到前缀函数π,π包含有模式自身位移进行匹配的信息,这些信息可避免朴素字符串搜索中的无用位移测试,安全的一次步进多个位移,进而减少匹配次数。KMP算法的核心是计算得到前缀函数π,得到π函数之后的搜索过程,反而显得很简单。,KMP算法总体具有线性时间复杂度,其中预处理阶段为 O(m),搜索阶段的最坏和平均时间复杂度均为 O(n),总时间复杂度为 O(n+m)。


2. 前缀函数 Π

先假设已经读入Text[i..i+k] 处长度为k (k>=0) 的字符串u与模式串前缀匹配,并且文本字符串i+k+1处字符α与模式字符串k+1处字符β失配。如下图Figure-01所示。

此时最简单的处理方案是将搜索窗口向右移动一位,再次从模式串第一个字符开始比对。Brute Search便是采用上述方法实现,类似这样的方式没有充分利用已经匹配过的字符信息。KMP算法通过预先处理模式,可以充分利用已成功匹配的信息,减少匹配次数,其预处理时间复杂度为O(m),搜索阶段时间复杂度为O(n),总时间复杂度为O(m+n)。

 

KMP算法的原始思想源于Morris and Pratt的论文 [MP70],如下(参考图Figure-02):
    • 对于模式的每个前缀u预先计算最长边界b(u)。
    • 在当前文本位置,设u是p的最长前缀,同时也是t1…ti的后缀。读入文本字符σ = ti+1。如果σ = p∣u∣+1,此时最长前缀是up∣u∣+1。但是如果σ ≠ p∣u∣+1,比较σ与p∣b(u)∣+1,如果σ = p∣b(u)∣+1,则b(u)p∣b(u)∣+1是新的p的最长前缀,同时也是t1…ti ti+1的后缀。如果σ ≠ p∣b(u)∣+1,比较σ = p∣b(b(u))∣+1,并循环,直到σ的一个边界或者没有任何边界(空边界),此时新的最长前缀为空字符串。

Knuth提出了一个改进。如果σ = ti+1和p∣u∣+1匹配失败,则u的边界的任何后续字符不能等于p∣u∣+1否则和σ不匹配。因此在预处理阶段,对于p的每个真前缀u (p = uw, w ≠ ∊),可以预先计算最长边界v使得p∣u∣+1 ≠ p∣v∣+1。(如同中所示即为γ≠β。)
我们可以用数学语言描述前缀函数π[q] = max { k: k < q and pk⊐ pq }
可见,为了得到前缀函数π,需要对于模式P的每个前缀u计算最长边界max{b(u)},使得p∣u∣+1 ≠ p∣v∣+1。当逐字符读入模式p1…pm,并计算p1…pi的最长边界时,事实上需要计算的最长后缀pj…pi (1≤j≤i) 也是p的前缀。因此,我们可以将KMP算法应用于其本身。KMP的预处理阶段可由KMP来完成,其时间复杂度为O(m) 。

 

3. 匹配过程

得到前缀函数π之后,从左到右扫描文本串T进行匹配。假定当前P[1..j]和T[i-j..i]匹配,继续检测T[i+1]和P[j+1],此时分两种情况:
    • T[i+1]=P[j+1]。令i=i+1,j=j+1。如果j等于模式串长度,则匹配成功返回;如果i等于文本串长度,则匹配失败返回;否则 继续执行下次比较。
    • T[i+1]≠P[j+1]。按照如下循环过程执行匹配检测(循环结束后,让模式串的位置标记j=k):
      1. 令k=π[j],如果k=1则结束循环。
      2. 由前缀函数的特性,可知P[1..k]⊐P[1..j]。由于T[i-j..i]和P[1..j]匹配,因此P[1..k]和T[i-k..i]匹配,可以直接进行P[k+1]和T[i+1]的匹配检测。
      3. 如果P[k+1]= T[i+1],则结束循环;否则继续执行1。
      如下Figure-03展示了某此失配后的处理。注意,需要迭代处理一直到P[k+1]=T[i+1]或者k=1。

KMP 算法在搜索阶段,最坏和平均时间复杂度都是 O(n)。

 

4. 实现代码
实现代码分三部分:
yc_kmp_init:  生成前缀函数π
yc_kmp_find: 匹配过程
yc_strstr_find: 封装strstr一致接口。
对于多次搜索某确定模式的问题,一般会生成前缀函数一次并保存,然后直接调用yc_kmp_find实现,这样可以省去多次计算同一前缀函数的问题。

 

5. 结束语
感谢您阅读本文,您可以任意转载、更改、或者本文的任意部分用于任何用途(包括商业用途)。转载请注明出处。
欢迎指出BUG、缺陷及相关问题。十分感谢。

您也可以到我的下载资源下载PDF格式的文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值