kmp匹配算法其精髓就在于next数组的获取和采用。当然next数组的生成对于很多的人短暂理解可能很好接受,但是时间一久就会忘记,接下来用简短的代码和案例带大家领路next数组的魅力。
首先,我的思路中将查询字串数组的第一位(数组下标为零)的值定义为整个数组的长度。
T | 8 | a | a | c | a | b | d | c | a |
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
next数组生成的精髓就在于回溯,下面我将根据代码进行详解,最后采用案例进一步剖析
//T为待匹配字符串,生成其对应的next下标数组。
void get_next(String T,int *next)
{
int j = 0;
int i = 1;//因为把T的零位用来存储数组长度了,所以从下标一开始
next[1] = 1;
while(i < T[0])//遍历T数组下标
{
if( 0 == j || T[i] == T[j] )//0==j是一种习惯,可以最快时间的发现j是否被定义
{
i++;
j++;
next[i] = j;
} else {
j = next[j];
}
}
}
首先定义i和j,i用来遍历T数组一直往后走,j用来实现回溯效果。
T | 8 | a | a | c | a | b | d | c | a |
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
首先,T[1]的下标为零,因为T数组我们将其的第零位放置了长度,所以next数组的第一位匹配不到的时候要采用j=next[j]进行定位下次T数组的位置,要定位到第一位。
T | 8 | a | a | c | a | b | a | a | c |
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
执行if之前j的值 | 0 | 0 | 0 | 1 | 1 | 2 | 1 | 2 | 3 |
next | x | 1 | 1 | 1 | 2 | 1 | 2 | 3 | 4 |
j | 0 | 0 | 1 | 1 | 2 | 1 | 2 | 3 | 4 |
当数组下标i为5时因为其前一位和第一位相同,所以下次比较只需要从第二位开始即可,然后将T[5]定为比较的第一位与开头进行比较。
对比的精髓就是将第i位分别作为起始位,对比其后多少位与从数组开头开始算往后相等
即:
a | c | d | a | c | e | e | f | |
a | c | d | a | c | f | |||
a | c | d | a | c | f |
解释为,下面为匹配数组,上面为原数组,对于下面数组可以观察到,acd和acf前两位一样,当f处出现匹配错误的时候,我们则不需要再比较ac,只需要定位到第三位即可。
int kmp(String s ,String T , int pos)
{
int i = pos;
int j = 1;
int next = [255];
get_next(T,next);
while (i <= S[0] && j <= T[0])
{
if(s[i] = T[j])
{
i++;j++;
} else {
J = next[j];
}
}
if(j > T[0])
{
return i - T[0];
} else {
return 0;
}
}