您的位置:首页 > 其它

KMP实现

2015-09-11 17:00 218 查看

代码

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;
}
}


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为例)

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;
}
}


注意:

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

该版本的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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: