本文主要介绍KMP算法原理。KMP算法是一种高效的字符串匹配算法,通过对源串进行一次遍历即可完成对字符串的匹配。

1、基础知识的铺垫

字符串T的前k(0 =< k <=tlen)个连续的字符串称为T的前缀(假如T的长度为tlen),则当k<tlen时,其前缀称为真前缀。同理,字符串T的后k个连续的字符串称为T的后缀,k<tlen时其后缀称为真后缀。

假如现在有字符串str=”abbaba”。则该字符串的真前缀有:a, ab, abb, abba, abbab. 其真后缀有:a, ba, aba, baba, bbaba。前缀包括源字符串本身,但真前缀不包括,后缀也一样。空字符串是任何字符串的前缀和后缀。

2、前缀函数next的计算

若一个字符串的真前缀等于后缀,我们称它为相等前后缀(为了表达方便,就用相等前后缀表示前后缀相等的前缀,这是本人取得名字,非专业术语)。

前缀函数next的计算就是计算最大相等前后缀的长度。比如字符串p=”ababaca”中,p[3]的最大相等前后缀为ab。next[3]=len(“ab”)=2。对字符串中每个字符计算next就可以得到一个next数组。

那么如何计算next数组呢?假如现在需要计算p[i]的next值,即next[i]。在计算next[i]时,前面的next[0]~next[i-1]已经计算了出来,这时,其计算过程如下:

令k=next[i-1]。若k>0&& p[i]!=p[k]。则k=next[k-1]。因为数组的下标是从0开始的,所以在比较时使用p[i]与p[k] 比较而不是p[k+1],而在重新获取k的值时,使用next[k-1],因为next数组是从0开始的,并且字符串数组也是从0开始计数的,书本上介绍的基本都是从1开始计数的,这是本文与书本之间存在的一点小小区别,但核心思想一样。

关于这点,更通俗的说法是:在计算next[i]时,因为前面的next[i-1]指出了前i-1个字符串的最大相等前后缀。为了计算字符串p[i]的最大相等前后缀的长度,若字符串p[0,1,…, i-1]的最大相等前后缀为p[0,1,…, k-1](k=next[i-1])。此时若p[k]==p[i],则next[i]=k+1。因为这时p[0,1,…, k]==p[i-k, i-k+1, …, i]。

若p[k]!=p[i],为区别起见,我们用q=next[k-1]。如下图所示,此时比较p[i]与p[q]即p[next[k-1]]。因为p[0, 1, .., k-1]==p[i-k, i-k+1,…, i-1],p[0, 1,…, q-1 ] == p[k-q, k-q+1, .., k-1]。若此时p[i] == p[q],则p[i]的最大相等前后缀的长度next[i] = q+1。否则, 如果p[i]!=p[q],则重复此过程,直到q=0。

关于前缀函数的正确性,算法导论有详细的证明,在此略过。

其前缀函数源代码如下:

int *getprefix(char p[])
{
int *h, plen, k, i;
plen = strlen(p);
h = (int *)malloc(plen*sizeof(int));
h[0] = 0;
k=0;
for(i=1; i<plen; i++)
{
while(k>0 && p[k]!=p[i])
k=h[k-1];
if(p[k] == p[i])
k++;
h[i]=k;
}
return h;
}

3、KMP匹配算法的实现

在计算好前缀数组next之后,就可以开始对源字符串进行比对,从源字符串的第0个到最后一个依次进行遍历。这个过程类似计算next数组。只是计算前缀函数的时候是自己与自己进行比较,对每个源串中的字符,若模式串中相应的字符与之不相等,则根据next数组确定模式串中下一个进行比较的字符。

若源串中的字符与模式串中相应的字符相等,则对两个串中的下一个字符进行比较,依次对源串进行一次遍历。

若有模式串匹配成功,则根据next数组将模式串的位置移动指定地方,继续进行比对。

KMP匹配代码如下:

int kmp_matcher(char t[], char p[])		//返回源串中模式串出现的次数
{
int count;
int *h, tlen, plen, i, q;
tlen = strlen(t);
plen = strlen(p);
h = getprefix(p);
count = 0;
q=0;
for(i=0; i<tlen; i++)
{
while(q>0 && t[i]!=p[q])
q = h[q-1];
if(t[i] == p[q])
q++;
if(plen == q)
{
count++;
q = h[q-1];
}
}
return count;
}

最新文章

  1. 利用cytoscape做网络图
  2. 【开园 and 计划】
  3. C#----XML操作小结
  4. Java加密算法 RSA
  5. TinyMCE textarea 输入框外部程序动态修改方法
  6. loj 1168(Tarjan应用)
  7. SQLServer使用表值参数,高性能批量插入数据
  8. BCB一个问过100遍啊100遍的问题
  9. 高性能javascript
  10. iOS 图片填充 UIImageView
  11. Python开发工具Wing IDE发布5.0.1版本
  12. Java Integer类型比较
  13. Nginx拦截算法
  14. 用canvas给视频图片添加特效
  15. robot framework关键词记录单(更新中)
  16. 远程连接ubuntu mysql出现2003错误 cant connect to mysql(转载)
  17. RabbitMQ之路由键转发消息
  18. 记录安装 java 环境,部署环境变量遇到的小坑
  19. JS种this的四种用法
  20. 【tensorflow】1.安装Tensorflow开发环境,安装Python 的IDE--PyCharm

热门文章

  1. C#入门经典(2-重置窗体布局,界面介绍,错误列表)
  2. CentOS/RHEL 7中的firewall控制
  3. Hibernate的generator属性
  4. Struts2--Action属性接收参数
  5. 安卓图表引擎AChartEngine(四) - 源码示例 嵌入Acitivity中的折线图
  6. C#设置word段落首行缩进为0
  7. astah* professional 6.9.0
  8. extJS4.2.0 环境搭建教程(一)
  9. jquery中:input和input的区别分析
  10. ubuntu下如何安装和卸载wine-qq