数据结构与算法:KMP匹配算法

##KMP 算法

基本思想

kmp

  • 我们来观察一下朴素的字符串匹配算法的操作过程。如下图(a)中所描述,在模式 P = ababaca 和文本 T 的匹配过程中,模板的一个特定位移 s,q = 5 个字符已经匹配成功,但模式 P 的第 6 个字符不能与相应的文本字符匹配。

  • 此时,q 个字符已经匹配成功的信息确定了相应的文本字符,而知道这 q 个文本字符,就使我们能够立即确定某些位移是非法的。例如上图(a)中,我们可以判断位移 s+1 是非法的,因为模式 P 的第一个字符 a 将与模式的第二个字符 b 匹配的文本字符进行匹配,显然是不匹配的。而图(b)中则显示了位移 s’ = s+2 处,使模式 P 的前三个字符和相应的三个文本字符对齐后必定会匹配。KMP 算法的基本思路就是设法利用这些已知信息,不要把 “搜索位置” 移回已经比较过的位置,而是继续把它向后面移,这样就提高了匹配效率。

算法原理

  • 已知模式 P[1…q] 与文本 T[s+1…s+q] 匹配,那么满足 P[1…k] = T[s’+1…s’+k] 其中 s’+k = s+q 的最小位移 s’ > s 是多少?这样的位移 s’ 是大于 s 的但未必非法的第一个位移,因为已知 T[s+1…s+q] 。在最好的情况下有 s’ = s+q,因此立刻能排除掉位移 s+1, s+2 … s+q-1。在任何情况下,对于新的位移 s’,无需把 P 的前 k 个字符与 T 中相应的字符进行比较,因为它们肯定匹配。

  • 可以用模式 P 与其自身进行比较,以预先计算出这些必要的信息。例如上图(c)中所示,由于 T[s’+1…s’+k] 是文本中已经知道的部分,所以它是字符串 Pq 的一个后缀。

  • 此处我们引入模式的前缀函数 π(Pai),π 包含有模式与其自身的位移进行匹配的信息。这些信息可用于避免在朴素的字符串匹配算法中,对无用位移进行测试。
    π [ q ] = m a x k : k < q a n d P k ⊐ P q π[q] = max {k : k < q and Pk ⊐ Pq} π[q]=maxk:k<qandPkPq

  • π[q] 代表当前字符之前的字符串中,最长的共同前缀后缀的长度。(π[q] is the length of the longest prefix of P that is a proper suffix of Pq.)
    下图给出了关于模式 P = ababababca 的完整前缀函数 π,可称为部分匹配表(Partial Match Table)。

计算过程:

  • π[1] = 0,a 仅一个字符,前缀和后缀为空集,共有元素最大长度为 0;
  • π[2] = 0,ab 的前缀 a,后缀 b,不匹配,共有元素最大长度为 0;
  • π[3] = 1,aba,前缀 a ab,后缀 ba a,共有元素最大长度为 1;
  • π[4] = 2,abab,前缀 a ab aba,后缀 bab ab b,共有元素最大长度为 2;
  • π[5] = 3,ababa,前缀 a ab aba abab,后缀 baba aba ba a,共有元素最大长度为 3;
  • π[6] = 4,ababab,前缀 a ab aba abab ababa,后缀 babab abab bab ab b,共有元素最大长度为 4;
  • π[7] = 5,abababa,前缀 a ab aba abab ababa ababab,后缀 bababa ababa baba aba ba a,共有元素最大长度为 5;
  • π[8] = 6,abababab,前缀 … ababab …,后缀 … ababab …,共有元素最大长度为 6;
  • π[9] = 0,ababababc,前缀和后缀不匹配,共有元素最大长度为 0;
  • π[10] = 1,ababababca,前缀 … a …,后缀 … a …,共有元素最大长度为 1;

算法原理

  • KMP 算法 KMP-MATCHER 中通过调用 COMPUTE-PREFIX-FUNCTION 函数来计算部分匹配表。
 1 KMP-MATCHER(T, P)
 2 n ← length[T]
 3 m ← length[P]
 4 π ← COMPUTE-PREFIX-FUNCTION(P)
 5 q ← 0                          //Number of characters matched.
 6 for i ← 1 to n                 //Scan the text from left to right.
 7     do while q > 0 and P[q + 1] ≠ T[i]
 8             do q ← π[q]        //Next character does not match.
 9         if P[q + 1] = T[i]
10             then q ← q + 1     //Next character matches.
11         if q = m               //Is all of P matched?
12             then print "Pattern occurs with shift" i - m
13             q ← π[q]           //Look for the next match.
 1 COMPUTE-PREFIX-FUNCTION(P)
 2 m ← length[P]
 3 π[1]0
 4 k ← 0
 5 for q ← 2 to m
 6      do while k > 0 and P[k + 1] ≠ P[q]
 7             do k ← π[k]
 8         if P[k + 1] = P[q]
 9            then k ← k + 1
10         π[q] ← k
11 return π
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tomorrowave

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值