KMP实现

代码

  1. Version 1
/*
The length of p(attern) is len, 
the length of next array is (len+1).
*/
void preKMP_one(char *p, int *next, int len){
    int i = 0, j = -1;
    next[0] = -1;
    while (i < len){
        while(j >= 0 && p[i] != p[j])
            j = next[j];
        i++; j++;
        next[i] = j;
    }
}
  1. Version 2
/*
The length of p(attern) is len, 
the length of next array is (n+1).
*/
void preKMP_two(const char *p, int *next, int len){
    next[0] = next[1] = 0;
    for(int i = 1; i < len; i++){
        int j = next[i];
        while(j != 0 && p[j] != p[i])
            j = next[j];

        if(p[i] == p[j])
            next[i+1] = j+1;
        else
            next[i+1] = j;
        // 
        /***The same as***
        if(p[i] == p[j])
            next[i+1] = j+1;
        else
            next[i+1] = 0;  // Note that j == 0 under this condition.
        ***/
        // Also the same as
        // next[i+1] = p[i] == p[j] ? j+1:j;
        // or next[i+1] = p[i] == p[j] ? j+1:0;
    }
}

比较

  • 跳转表(next数组)长度为n+1,跳转表最后一项,表示当该pattern匹配完全后,继续匹配时需要从pattern的哪个位置开始匹配,这一项对于需要连续查找匹配的情况很方便;

  • 两者都是计算next表下一项的值(相对于内部while循环时的i);

  • 1中由于内部的while循环之后,不做判断(j是否为0),直接进行i++,j++,因此需要设置next[0]==-1防止死循环(j = next[j] -> 0 = 0)

  • 从上一条可以看出,若将1中内部while循环之后的语句(i++;j++;next[i]=j)改为条件判断然后赋值,则1和2将几乎相同;

  • 本质上,内部的while循环跳出的条件有两个: j 和 p[i] == p[j]。当p[i] == p[j]时,p[i+1] == j+1;而当P[i] != pj时,则p[i+1] = 0(此时1中的j=-1,2中的j=0)。

  • next的意义:当出现text[j] != p[i]时,下一次的比较应该在text[j]和p[next[i]]之间进行。next[i]表示p[i]的前缀(p[0..i-1])中头和尾相等部分的长度,即,设next[i] = k,则p[0..k-1] = p[i-j..i-1]。比如next[3] = 1,则p[0] == p[2]。

优化

可进行优化如下(以Version 1为例)

  1. Version 3
/*
The length of p(attern) is len, 
the length of next array is (len+1).
*/
void preKMP_three(char *p, int *next, int len){
    int i = 0, j = -1;
    next[0] = -1;
    while (i < len){
        while(j >= 0 && p[i] != p[j])
            j = next[j];
        i++; j++;

        // optimization
        if (p[i] == p[j])
            next[i] = next[j];
        else
            next[i] = j;
    }
}

注意:

  1. 该版本中,计算至最后,p的下标实际上会越界(12行if条件的判断中),但此时i=len, p[len]的值为’\0’,因此并不会产生错误,但需要注意;

  2. 该版本的next数组中可能会有很多的-1(除next[0]外),这些-1表示:直接比较text中下一个字符(text[j+1])和p[0],而非非优化版中的继续比较text[j]和p[next[i]]。

KMP-Match

int kmp(const char *text, int tlen, const char *p, int plen) {
    int *next = new int[plen+1];
    if(!next)
        return -1;

    //preKMP_one(p, next, plen);
    preKMP_three(p, next, plen);

    int i = 0, j = 0;
    while(i < tlen && j < plen){
        if(j == -1 || text[i] == p[j])
            i++, j++;
        else
            j = next[j];
    }

    delete [] next;
    if (j >= plen)
        return i-plen;
    else
        return -1;
}

KMP for Version 2

int kmp(const char *text, int tlen, const char *p, int plen) {
    int *next = new int[plen+1];
    if(!next)
        return -1;

    preKMP_two(p, next, plen);

    int i = 0, j = 0;
    while(i < tlen && j < plen){
        if(!j || text[i] == p[j])
            i++, j++;
        else
            j = next[j];
    }

    delete [] next;
    if (j >= plen)
        return i-plen;
    else
        return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值