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;
}
KMP算法是一种字符串匹配算法,可以在一个文本串中查找一个模式串是否存在。它的时间复杂度为O(n+m),其中n和m分别是文本串和模式串的长度。 KMP算法的核心思想是利用已经匹配的部分字符信息来避免重复匹配。具体实现过程如下: 1. 预处理模式串,得到next数组。next[i]表示当第i个字符不匹配时,模式串应该从哪个位置开始继续匹配。 2. 在文本串中进行匹配。从文本串的第一个字符开始,依次与模式串的字符进行匹配。如果匹配成功,则继续匹配下一个字符。如果匹配失败,则根据next数组将模式串向右移动一定的距离,然后再从当前位置重新开始匹配。 下面是KMP算法的C语言实现: ``` #include <stdio.h> #include <string.h> void getNext(char* pattern, int* next) { int len = strlen(pattern); int i = 0, j = -1; next = -1; while (i < len) { if (j == -1 || pattern[i] == pattern[j]) { i++; j++; next[i] = j; } else { j = next[j]; } } } int kmp(char* text, char* pattern) { int tlen = strlen(text); int plen = strlen(pattern); int i = 0, j = 0; int* next = (int*)malloc(plen * sizeof(int)); getNext(pattern, next); while (i < tlen && j < plen) { if (j == -1 || text[i] == pattern[j]) { i++; j++; } else { j = next[j]; } } free(next); if (j == plen) { return i - j; } else { return -1; } } int main() { char text[] = "hello world"; char pattern[] = "world"; int pos = kmp(text, pattern); if (pos != -1) { printf("pattern found at position %d\n", pos); } else { printf("pattern not found\n"); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值