KMP算法详解(易理解)

前言:

断断续续得学习KMP算法已经有些时间,每次看这个算法都会有更进一步得理解。至今,总算对整个KMP算法有了个完整的理解。

简单模式匹配算法:KMP算法的产生是源于字符串模式匹配问题的。因为原始的朴素模式匹配算法太过粗糙,效率低所以才有科学家发明了KMP算法的。朴素的模式匹配算法流程:将模式的首位与主串首位比较如果相等则比较下一位,否则主串往后跳一位重新执行前面的操作。直到主串跳完或者模式完全匹配完。结果有两种:①模式与主串匹配成功,此时便可找到首次匹配的位置。②主串中无与模式匹配的子串朴素模式匹配算法C语言代码如下:

int index(char S[],char T[],int pos){
//如果成功匹配则返回首次匹配的子串在主串的位置,否则返回0
//pos为主串起始查找位置
int i=pos;
int j=1;
while(i<S[0]&&j<[0]){
if(S[i]==T[i]){++i;++j}
else
{
i=i-j+2;//向后跳一位
j=1;//重新从首位开始匹配
}
}

if(j>T[0]) return i-T[0];
else
return 0;
}

正文: 

  1. KMP算法思想:在主串与模式串比较出现失配时,不必像朴素模式匹配算法那样从头开始。而是利用已经部分匹配的成果,考虑主串此时的S[i]应该与模式的那个字符进行比较。示例:S=‘ababefefg’(主串) T='ababfad' (模式)用朴素模式匹配算法对上述主串和模式进行模式匹配,当S[5]与T[5]失配时,是要将主串向后跳一位即从S[2]重新开始与模式进行匹配,而KMP算法的思想是利用部分匹配的成果即主串和模式的abab部分此时考虑主串的S[5]应该与T[3]比较(这里这个问题显而易见),就此我们可以看出朴素匹配算法需要经过多次往复才能到KMP算法一次判断的位置。所以KMP模式匹配的算法思想实际上是利用部分匹配的成果将模式往后滑行尽可能远的距离,避免朴素模式匹配算法中的重复比较

  2. KMP算法核心问题:如上所说KMP算法的核心问题显然就是计算当出现失配情况时,主串应该与模式的哪个位置进行比较了。我们假设应该与第k个位置进行比较,根据KMP算法的思想可以得到如下式子(设Si代表主串字符,Pi代表模式字符):'P1P2...Pk-1'='Si-k+1...Si-1'① 而根据已经部分匹配的结果可以有:'Pj-k+1...Pj-1'='Si-k+1...Si-1'② 根据上面二式我们可以得到下面的式子:'P1P2...Pk-1'='Pj-k+1...Pj-1' 当然要符合KMP算法的:尽可能滑得远的思想,此时就不可能存在k'>k使得上式成立。

  3. KMP算法之next[]数组求算:既然得到了上面得式子,我们所要解决的问题就只剩下了根据这个式子把每一个位置的失配之后的下一比较位置求解出来,我们称整个模式所有位置对应的下一位置为next[]数组。我们的思想是从第一个字符开始逐个求解出每一个字符的next。在解决这一整个问题之前我们先思考这个问题:当已经有'P1P2...Pk-1'='Pj-k+1...Pj-1',我们考虑下一位:Pk和Pj的匹配情况。若Pk=Pj显然根据上面的成果,我们可以得出next[j+1]=next[j]+1。若不等于呢?我们应该怎么确定next[j+1]呢?此时,这个子问题又相当于一个模式匹配问题了,此时Pj就应该和Pnext[k]比较了,如果不相等继续往复,如果1~j间无任何字符可匹配则next[j+1]=1;将上述算法写成C语言代码如下:

    void getNext(char T[],int next[]){
    int i=1;int j=0;next[1]=0//我们规定1位的next为0,即是将模式在主串后移动一个位置重新进行匹配。
    while(i<T[0]){
    if(j==0||T[i]==T[j]){
    ++j;++i;
    }else{
    j=next[j];
    }
    }
    }

     

  4. KMP算法之nextval[]数组求解:上面的next[]数组其实还不够精致,因为当Pj位与主串失配时,如果Pj=Pk 的话就没有匹配意义了,此时next[j]=next[k];我们称这样求解出来的数组为nextval[]数组。nextval[]数组求解方法C语言代码如下:

    void getNextval(char T[],int nextVal[]){
    int i=1;int j=0;nextval[1]=0;
    while(i<T[0]){
    if(j==0||T[i]==T[j]{++i;++j
    if(T[i]!=T[j])next[i]=j;
    else{
    next[i]=next[j];
    } 
    }
    else{
    j=next[j]
    }
    }
    }

     

结尾:

附上使用kmp算法的模式匹配代码:

int KMP_index(char S[],char T[],int pos){
//如果成功匹配则返回首次匹配的子串在主串的位置,否则返回0
//pos为主串起始查找位置
int i=pos;
int j=1;
while(i<S[0]&&j<[0]){
if(S[i]==T[i]){++i;++j}
else
{
j=next[j]//or j=nextval[j]
}
}

if(j>T[0]) return i-T[0];
else
return 0;
}

自觉理解尚未透彻,若有理解错误或不足之处,期待各位阅者指正,帮助加深我的理解。 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值