KMP
KMP也就是朴素匹配算法的优化,加快了匹配效率。
他的关键点就在于:问题是由模式串(字串)决定,而不是目标串决定。
他的核心就在于:减少不必要的回溯
回溯:当匹配时失配后应该用子串中的哪个元素继续和目标串匹配。
匹配过程讲解
例如:
S为目标串,T为模式串(子串),对于朴素的BF算法来说,当匹配到第五个元素的时候,出现了失配的现象,那我们该如何解决,也就是减少不必要的回溯,通过我们的计算来减少:
我们可以看到,当匹配到第五个元素的时候,如果是BF算法的话,大家都知道是要i2和j1匹配,但是我们可以通过计算发现这是不必要的回溯:
j1=i1,j2=i2;
我们通过模式串可以明显地看出:j1!=j2;所以推出j1!=i2;同理可得j1!=i3;j1!=i4;
所以我们可以直接跳过中间的回溯,直接让j1和i5匹配:
像这样,我们通过减少不必要的回溯就可以大大优化这个匹配算法,这就是KMP了。
当然,大家都知道,KMP算法最重要的是写出next数组,也就是如果出现失配,字串应该回溯到哪个元素的一个数组。
通过上面的例子,我们可以很快地写出T的next数组:
next数组求解方法
再举一个例子:
next数组从1开始第一个元素一定是0,第二个元素一定是1,第三个元素我们发现前面两个元素并不相等,所以如果在第三个元素失配的情况下,直接回溯到第一个元素:
i1=i3,所以next[4]=2;
i1i2=i3i4;所以next[5]=3;
i1i2i3=i3i4i5;所以next[6]=4;
i1=i6;所以next[7]=2;
i1=i7;所以next[8]=2;
i1i2=i7i8;所以next[9]=3;
我们需要判断这个元素之前有多少个前缀与后缀相同的元素,再加一就得到了next数组。
代码时刻
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
typedef char* string;
int get_next(string T,int* next) {
int i = 1,
j = 0;
next[1] = 0;
while (i < T[0]) {
if (0 == j || T[i] == T[j]) {
i++;
j++;
if (T[i] != T[j]) {
next[i] = j;
}
else {
next[i] = next[j];
}
}
else {
j = next[j];
}
}
return 0;
}
int KMP(string S,string T,int* next) {
int i = 0,
j = 0;
while (i < S[0] && j < T[0]) {
if (0 == j||S[i] == T[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j >= T[0]) {
return i - T[0] + 1;
}
else
return -1;
}
int main(void) {
char S[255],T[255];
int next[255];
int value;
printf("请输入主串字符串:");
scanf_s("%s", S + 1,255);
printf("\n请输入子串:");
scanf_s("%s", T + 1,255);
T[0] = 0;
for (int i = 1; T[i]; i++) {
T[0]++;
}
S[0] = 0;
for (int i = 1; S[i]; i++) {
S[0]++;
}
get_next(T,next);
value = KMP(S,T,next);
if (value != -1)
printf("\n%d\n", value);
else
printf("主串中没有子串");
return 0;
}
今天就不讲解代码啦!大家可以用自己的思路先尝试一下。
当然没有调试结果的代码就不是好代码:
总结
下次就学树啦!我们下次再见!