目录
概述
什么是next什么是nextval?
next:指前后缀,公共最大长度。
nextval:指为了模式匹配算法,每次匹配不成功只移动一位,实际上可能可以移动更,造成额外的开销,实现最大程度上的移动。
next
例如:ababa(求next数组值)
看最大匹配长度,也就是相同字符的最大匹配的长度(例如abab:最大匹配长度就是2(ab:ab))
这里有的人计数是从-1开始的,那么从-1开始还是0开始,是没有区别的,记录而已。
模式的子串 | 前缀 | 后缀 | 公共前后缀的最大长度next |
a | 无 | 无 | 0 |
ab | a | b | 0 |
aba | a,ab | a,ba | 1 |
abab | a,ab,aba | b,ab,bab | 2 |
ababa | a,ab,aba,abab | a,ba,bab,baba | 1 |
nextval
出现nextval的原因,是针对KMP算法的改进(主要是因为回溯得过多,造成不必要的时间开销了)。
那么究竟是为什么会回溯太多呢?
例如在:abab当中,next的值当然为2,那么要是abcbc当中呢?当next的数组无法找到匹配的字符串的时候,需要向后移动一位,但是此时存在的情况是这几位都不满足,一次移动一位,又在循环一次计算next,比较串,那么会增加系统的开销。时间复杂度也为O(m*n)。
所以kmp算法提出了,nextval最大程度移动更多,时间复杂度能够达到O(m+n)。
next和nextval的计算
总的来说nextval的值有一个公式:nextval[i]=nextval[next[i]]。
a的next值为1他就回到第一个位置处,而第一个位置处的next值为0所以他的nextval值也为0.如下图所示:
再看第四个数b,他的next值为为2,找到第二个元素,他的next值为1,找到第一个元素,由于第一个元素为a与b不同则,b的nextval值为1.
经过上述步骤的计算之后,最后是能够得到如下结果的
一图了解
下面这个流程图,就可以更直观了解到next和nextval的计算。
实例练习
我们以一道408考研的真题为例:
当匹配到第六位的时候,匹配失败
然后回去查找C的next值(3)
从模式串的第三位开始匹配,从主串的第六位开始匹配。
然后就匹配成功,一共比较次数为10次。
如下图
代码实现
//定义结构体
typedef struct
{
char *ch;
int length;
}str;
//计算next
void calculate_next (str substr,int next[])
{
int i=1,j=0;
next[1]=0; //为什么从0开始,因为next[0],无论如何都是等于0的
while(i<substr.length)
{
if(j==0||substr.ch[i]==substr.ch[j]) //如果j==0表示没有了,直接向后移动,substr.ch[i]==substr.ch[j]表示当前位次匹配成功
{
++i; // 匹配成功之后,向后移动继续匹配最大公共长度
++j;
next[i]=j; //j用于记录前后缀,最大公共长度
}
else
j=next[j]; //匹配不成功,那么j=next[j],j的值回溯
}
}
int KMP(str str1,str substr,int next[])
{
int i=1,j=1; //j是当前字串中下标位置,i是当前字串中主串的下标位置
calculate_next(substr,next); //得到next数组
while(i<str1.length && j<substr.length) //如果小于字符串的长度,则循环比较
{
if(j==0 || str1.ch[i] == substr.ch[j]) //两个字母相等则继续匹配
{
++i;
++j;
}
else
j=next[j]; //匹配不成功,回溯
}
if(j>substr.length)
return i-substr.length;
else
return 0;
}
总结
kmp算法,要记得
- next是前后缀最大公共长度。
- nextval[i]=nextval[next[i]]。
- 代码理解,能看懂就可以了吧。