此篇文章总结的 基于kmp中的模式串方式
关于next图解如下
举例:字符串 “ababaa”
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
字符 | a | b | a | b | a | a |
next | 0 | 0 | 1 | 1 | 2 | 3 |
next定义写法
从索引1位置开始计算,除去第i位置,从0到 i-1,前后缀中最长重复字符长度
备注如果没有前后缀元素只要自己默认为0,如果没有相同元素默认为1
next[0]=0 默认0
next[1] =0 从1位置开始计算 默认为0
next[2] = 1,此时ab, 前后缀中没有公有为1
next[3] = 1,此时aba, 字符串前缀组合a,ab 字符串后缀组合 b,ba;前后公有最长a,所以为1
next[4] = 2,此时abab, 字符串前缀组合a,ab,aba, 字符串后缀组合 b,ab,bab;前后公有最长ab,所以为2
next[5] = 3,此时ababa, 字符串前缀组合a,ab,aba, 字符串后缀组合 b,ab,aba;前后公有最长aba,所以为3
求解代码如下:
/*
*@author: 赵秋然
*@date:2021年1月19日
*@description:kmp中next求解
*/
void kmp_next(char patt[], int next[])
{
int i = 0, j = 1, n = strlen(patt);
next[i] = next[j] = 0;
while (j < n)
{
if (i == 0 || patt[i] == patt[j])
{
++i;
next[j + 1] = i;
++j;
}
else
{
i = next[i];
}
}
}
nextval求解方法
nextval是在next方式上的升级改进,使得模式串回退跟快速有效,减少next中不必要的比较
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
字符 | a | b | a | b | a | a |
next | 0 | 0 | 1 | 1 | 2 | 3 |
nextval | 0 | 0 | 1 | 0 | 1 | 3 |
计算方式
默认前2位为0
nextval计算方式需要根据next结果
索引2时,元素a,next值为1,索引为1,元素b, a≠b ;nextval[2] = next[2]=1
索引3时,元素b,next值为1,索引为1,元素b, b=b ;nextval[3] = next[1] = 0
索引4时,元素a,next值为2,索引为2,元素a, a=a ;nextval[4] = next[2] = 1
索引5时,元素a,next值为3,索引为3,元素b, a≠b ;nextval[5] = next[5] = 3
根据以上结论得出:
当前索引与next索引值相同,此时索引值为next索引值
如果不同,此时索引值为next值
/*
*@author: 赵秋然
*@date:2021年1月21日
*@description:kmp中nextval求解
*/
void kmp_nextval(char patt[], int next[])
{
int i = 0, j = 1, n = strlen(patt);
next[i] = next[j] = 0;
while(j < n)
{
if(i == 0 || patt[i] == patt[j])
{
if(patt[j+1] == patt[i+1])
{
next[j+1] = next[i+1];
}
else
{
next[j+1] = i+1;
}
++i;
++j;
}
else
{
i = next[i];
}
}
}
为什么会引入nextval呢?
举例:
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
字符 | a | a | a | a | a | b |
next | 0 | 0 | 1 | 2 | 3 | 4 |
nextval | 0 | 0 | 0 | 0 | 0 | 4 |
模式串aaaaab,构建next数组后,对字符进行匹配时,如果发现字符不匹配那么next就需要回退
如果此时是索引4位置不匹配,那么根据next值就需要回退到3,2,1的不断回退,匹配速度变慢,效率下降
nextval方式直接回退到首位置,减少了不必要的回退