简单粗暴的理解KMP

先看看next数组的定义(假设模式串为p):
next[i] = -1 //当j=0时
next[i] = k //当”p[0]…p[k-1]” = “p[i-k]…p[i-1]”,即0-k处与后面部分对称 @
next[i] = 0 //其他情况

注意红色部分,结束位置为i-1不是i!!!,也就是说next[i]的值是由p[i-1]决定的

首先来看看一个例子:
p = “a b c d a a b b”
位置 0 1 2 3 4 5 6 7
下面是计算next数组的过程
1、对于i=0,next[i] = -1;
2、然后从i>0开始,首先看i-1和0是否相等,不等则next[i]=0;
这样可以得到next[1] = next[2] = next[3] = next[4] = 0;
3、对于i = 5:发现p(i-1)=p(1-1)但是p[i-2]!=p[2-1]因此next[5] = 1; 参考@处

4、接下来是重点,我们换个例子如下。请先牢记i-1决定next[i]的值。这次我们换一个比较快捷的比较方式,也是代码实现的方式,根据原来获知的部分对称的长度来推算.对于i=8,首先因为next[7]=1,而p[i-1=7] = p[1+1=2](代码中是用p[j]来表示,其实也就是上次找到的符合部分对称的结束位置),说明部分对称的长度应该增加,所以next[8]=2。但是对于i=12,我们知道next[11] = 5,但是p[12-1=11] != p[5+1=6],所以令j=next[5]也就是上一次符合部分对称的结尾,但是p[11]!=p[j],继续找部分对称的结尾位置直到开头或者p[11]=[[j]。

P a b c d e f a b c d e g f
位置 0 1 2 3 4 5 6 7 8 9 10 11 12
Next -1 0 0 0 0 0 0 1 2 3 4 5 0

代码:

#include<stdio.h>
#include<string.h>

//获取模式串(子串)的next值
void getNext(char *p,int next[]);
//进行匹配,若匹配不成功则返回-1
int getIndex(char *s,char *p,int pos,int next[]);

int main()
{
    char p[10] = "vuvrt";
    char s[25]  = "vvusdvudergvuvrtdiw";
    int next[5] = {0};

    getNext(p,next);
    printf("%d\n",getIndex(s,p,1,next));

    return 0;
}

//获取模式串(子串)的next值
void getNext(char *p,int next[])
{
    int i, j,len;
    next[0] = -1;
    j = -1;
    len = strlen(p);
    for(i=0;i<len;)
    {
        //和上一次的部分对称结尾的下一个位置进行比较
        if(j==-1 || p[i]==p[j])
        {
            i++;
            j++;
            next[i] = j;
        }else
        {
            //获取上上次部分对称的位置
            j = next[j];
        }
    }
    for(i=0;i<len;i++)
    {
        printf("%d\t",next[i]);
    }
    printf("\n");
}

//进行匹配,若匹配不成功则返回-1,从主串第pos个位置找
int getIndex(char *s,char *p,int pos,int next[])
{
    int i, j,slen,plen;
     i = pos-1;
     j = -1;
     slen = strlen(s);
     plen = strlen(p);
     while(i<slen && j<plen)
     {
         if( j==-1 || s[i]==p[j])
         {
             i++;
             j++;
         }else
         {
             //不匹配时i不回退,j回退
             j=next[j];
         }
     }
     if(j>=plen)
     {
         //子串能遍历完说明找到了
         return i-plen;
     }else
     {
         return -1;
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值