zoukankan      html  css  js  c++  java
  • KMP算法深入解析

    本文主要介绍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;
    }


  • 相关阅读:
    Apache CXF入门
    C++中关于类型转换的问题讨论
    makefile实例(3)-多个文件实例优化
    makefile实例(2)-多个文件实例
    makefile实例(1)-helloworld
    【原创】Linux下编译链接中常见问题总结
    tcpdump命令
    共享内存基础
    关于TCP/UDP缓存
    TCP/UDP常见问题小结
  • 原文地址:https://www.cnblogs.com/liuwu265/p/4100098.html
Copyright © 2011-2022 走看看