一道kmp模板题,发现我还是有一些细节没有理解到位
先简述一下kmp算法:给定一个文本串和一个模式串,求模式串在文本串中不重叠地出现了几次。若文本串和模式串中有许多部分是一样的,那么采用传统的方法就会有一些浪费。简单一点来说,kmp算法就是对模式串进行预处理,用一个next数组,next[j]的数值表示模式串的第j位之前的串中前多少位与最后多少位相等再加一,也就是如果第j位不匹配的话应该从第next[j]开始匹配。例如,
abcabx 对应的next数组的值为
011123
于是在匹配时
abcabc…
abcabx 发现不匹配
传统算法下
abcabc…
_abcabx
但在kmp中next[6] = 3,于是
abcabc…
___abcabx
我们已经知道第六位前面的两位ab与模式串头上的ab是一样的,于是就不用再检验一遍了,直接从第三位开始往后匹配。
这个算法还有可以改进的地方,我们让模式串不变,文本串变成
abcahx…
abcab… 匹配到这的时候发现不行,我们找next[5] = 2
abcahx
___ab… 由于前面是重复的,所以一定也不行
我们就直接在获得next数组的时候,如果t[i] == t[j]
让next[i] = next[j]
。 这里也就是next[5] = next[2] = 1
好了,算法理解了之后写题遇到了什么问题呢
我习惯让数组从0开始存,这样的话next数组的第一位为0就不行了,会产生死循环 把它置成-1就好啦
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
char a[1005], b[1005];
int nextval[1005];
void get_next(char *t, int *nextval, int size)
{
int i = 0, j = -1;
nextval[0] = -1;
while (i <= size)
{
if (j == -1 || t[i] == t[j])
{
i++;
j++;
if (t[i] != t[j])
{
nextval[i] = j;
}
else
{
nextval[i] = nextval[j];
}
}
else
{
j = nextval[j];
}
}
}
int main()
{
while (1)
{
scanf("%s", a);
if (a[0] == '#' && strlen(a) == 1)
{
return 0;
}
scanf("%s", b);
int sizea = strlen(a), sizeb = strlen(b);
int ans = 0;
get_next(b, nextval, sizeb);
// for (int i = 0; i < sizeb; i++)
// {
// printf("%d ", next[i]);
// }
// printf("\n");
int i = 0, j = 0;
while (i <= sizea)
{
if (j == -1 || a[i] == b[j])
{
if (j == sizeb - 1)
{
i++;
j = 0;
ans++;
}
else
{
i++, j++;
}
}
else
{
j = nextval[j];
}
if (j >= sizeb)
{
ans++;
j = 0;
}
}
printf("%d\n", ans);
}
return 0;
}
next被编译器认为时保留名称,所以就不能再作为数组名了,这个问题在我本地的gdb中没有报出来,交了好几个CE_(:з」∠)_,需要注意一下