KMP算法的精髓在于得到PMT(Partial Match Table)表格,有些同学使用next表示,这是因为在实际使用中使用的是pmt左移一个单位之后的表格。
要想得到PMT表格,首先需要知道以下几个概念:
子串:不包含字符串本身
前缀:字符串子串集合中以首字符开头的子串
后缀:字符串子串集合中以尾字符结束的子串
PMT:前缀集合和后缀集合中相同字符串的最大长度
举例:
A B C D A B D
A:无前缀和后缀
AB:前缀:A
后缀:B
前缀和后缀无相同字符串,则最大长度为0
ABC:前缀:A、AB
后缀:BC、C
前缀和后缀无相同字符串,则最大长度为0
ABCD:前缀:A、AB、ABC
后缀:BCD、CD、D
前缀和后缀无相同字符串,则最大长度为0
(有没有发现ABCD的前缀和ABC的前缀有一种关系,同样ABCD的后缀和ABC的后缀也有一种关系)
ABCDA:前缀:A、AB、ABC、ABCD
后缀:BCDA、CDA、DA、A
前缀和后缀共有字符串为A,则最大长度为1
ABCDAB:前缀:A、AB、ABC、ABCD、ABCDA
后缀:BCDAB、CDAB、DAB、AB、B
前缀和后缀相同字符串为AB、B,则最大长度为2
ABCDABD:前缀:A、AB、ABC、ABCD、ABCDA、ABCDAB
后缀:BCDABD、CDABD、DABD、ABD、BD、D
前缀和后缀无相同字符串,则最大长度为0
当比较到蓝色部分时,dst的ABCDAB的PMT值为2,即最前的两个字符与最后面的两个字符相等,
即可以确定src的中标黄色的部分和dst中标黄色部分相等,因此src标黄色的部分和dst标绿色的部分相等,
所以下次src标蓝色的部分和dst标红色的部分开始比较
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
/*
* 前缀:字符串子串集合中以首字符开头的子串
* 后缀:字符串子串集合中以尾字符结束的子串
* pmt中的值为前缀和后缀中共有的最大长度的值
*/
void GetPmt(char *string, int *pmt)
{
int i;
int commLengthId = 0;
int length = strlen(string);
pmt[0] = 0;
for (i = 1; i < length; i++) {
while ((commLengthId > 0) && (string[i] != string[commLengthId])) {
commLengthId = pmt[commLengthId - 1];
}
if (string[i] == string[commLengthId]) {
commLengthId++;
}
pmt[i] = commLengthId;
}
}
void GetNext(char *string, int *next)
{
int i;
int commLengthId = 0;
int length = strlen(string);
next[0] = 0;
next[1] = 0;
for (i = 1; i < length; i++) {
while ((commLengthId > 0) && (string[i] != string[commLengthId])) {
commLengthId = next[commLengthId];
}
if (string[i] == string[commLengthId]) {
commLengthId++;
}
next[i + 1] = commLengthId;
}
}
/* id: 0 1 2 3 4 5 6 7 8 9
* src: a b c a b a b a e f
* dst: a b a e f
* 当比较到第6位的时候,b 和 e不相等,dst中e前三位为aba,最大pmt值为1,
* 意味着b之前有一位至少是已经能对其的,下一次比较从dst中的b开始
* src: a b c a b a b a e f
* dst: a b a e f
* 返回找到目标字符串的原字符串起始下标
*/
int KmpPro(char *src, char *dst)
{
int i = 0;
int j = 0;
int srcLength = strlen(src);
int dstLength = strlen(dst);
int *next = (int *)malloc((dstLength + 1) * sizeof(int));
GetNext(dst, next);
while ((i < srcLength) && (j < dstLength)) {
if (src[i] == dst[j]) {
i++;
j++;
} else if (j == 0) {
i++;
} else {
j = next[j];
}
}
free(next);
next = NULL;
if (j == dstLength) {
return (i - j);
}
return -1;
}
int main()
{
freopen("test/1.txt", "r", stdin);
char src[100];
char dst[100];
scanf("%s", src);
scanf("%s", dst);
printf("%d\n", KmpPro(src, dst));
system("pause");
return 0;
}