KMP算法真的搞死人,终于搞懂了

现总结如下:
想理解KMP算法就要先看看最原始的模式匹配
int  match( char   * string char   * pat)   // 原模式匹配函数
{
    
int i = 0, j = 0;

    
while (i < strlen(string&& j < strlen(pat)) {
        
if (sting[i] == pat[j]) {
            i
++;
            j
++;        
        }
 else {
            i 
= i - j + 1;   //对string回溯
        }

    }

    
    
if (j = strlen(pat))
        
return i - strlen(pat);
    
else
        
return 0;
}


由于不必要的回溯增加了算法本身的时间复杂度,但完全不回溯就有可能漏了一个可能匹配成功的情况。
比如 : string = aaabababcaaadfe           不回溯就是这样   aaabababcaaadfe 
        pat =      ababc  //失配                                  ababc
所以如果完全不回溯就会漏了这个情况  aaabababcaaadfe
                                      ababc      //匹配成功
为什么会漏了那个情况呢?
其实这就是KMP研究的问题,是由于pat = ababc本身性质所决定,因为pat本身有重合的地方
那么KMP是怎么做的呢?
KMP是回溯pat串,而string串始终不回溯,这样的部分回溯就使得算法本身效率提高了很多,特别当string很长时
现在我们就可以来研究一下KMP的算法
我们想如果对于上面的例子,让j -= 2;i不变,是不是就是漏了的那种情况呢。显然这是对的,这样再继续下面的匹配函数过程,就不会有问题了。这就是KMP的算法所在。那怎么处理才知道什么时候让j减几呢?这就是KMP定义的failure function.
令P = P 0P 1P...P n-1是一个模式,则其失配函数f定义为:
f(j) = i 为满足 i < j且使得 P 0P 1...P i = P j-iP j-i+1...P j的最大整数 如果i >= 0
f(j) = -1
例如,对于模式pat = abcabcacab,有:
  j  0   1   2   3   4   5   6   7   8   9
pat  a   b   c   a   b   c   a   c   a   b
f(j) -1  -1  -1  0   1   2   3   -1  0   1

呵呵,是不是有点晕了,我看到这也晕了。没关系,继续,go ahead! Life will be better!比比先前我的那个想法,就会明白一些,也就是说,如果比较到了j = 6的位置,出现了问题,那么我们完全可以从j = 3的位置开始比较,也就是pat abcabcacab回溯了3个位置,而string当前i的位往前数3个位置肯定是abc。是不是明白了一些。
Anyhow,让我们先实现这个failure function再说
/*author: livahu
**2006.11.26
*
*/
void  fail( char   *  pat,  int   *  failure)  {
    
int i, j;
    
int n = strlen(pat);
    failure[
0= -1;  //定义最初情况
    for (j = 1; j < n; j++{
        i 
= failure[j - 1];
        
while ((pat[j] != pat[i + 1]) && (i >= 0))  
            i 
= failure[i];
        
if (pat[j] == pat[i + 1])
            failure[j] 
= i + 1;
        
else 
            failure[j] 
= -1;  //代表没有重合的,不能形成重合子串
    }
        
}
现在再看定义了failure function有什么用
当然是得到模式匹配的规则:如果出现了形如S i-j...S i-1 = P 0P 1...P j-1且S i != P j的部分匹配,那么,若j != 0, 则下一趟模式匹配时,从失配字符S i和模式串字符P f(j-1)+1处重新开始比较;而若j = 0时,则继续比较S i+1和P 0
现在实现模式匹配算法pmatch()
/*author: livahu
**2006.11.26
*/

include 
< stdio.h >
#include 
< string .h >
#define  MAX_STRING_SIZE 100
#define  MAX_PAT_SIZE 100

int  pmatch( char   *   string char   *  pat)
{
    
int failure[MAX_PAT_SIZE];
    fail(pat, failure); 
//得到对于pat不同位置的f函数值
    
    
int i = 0, j = 0;
    
int lens = strlen(string);
    
int lenp = strlen(pat);
    
    
while (i < lens && j < lenp) {
        
if (string[i] == pat[j]) {
            i
++;
            j
++;
        }
 else if (j == 0//string字符串往后移
            i++;
        }
 else {
            j 
= failure[j - 1+ 1;  //使模式串字符转到Pf(j-1)+1
        }

    }

    
    
return ((j == lenp) ? (i - lenp) : -1); //判断是否成功匹配
}
现在是不是很佩服D.E.Knuth,J.H.Morris和V.R.Pratt他们啊,其实很简单的原理,但就是他们才总结出来。
创新才是最难的!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值