#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
void cal_Next(char str[], int next[], int len) { //计算next数组,str为目标字符串,next为 next数组,len是字符串长度
//i表示当前位,j表示i前面某个前缀的最后一位的下标
int i, j;
//0位的next值表示0位之前没有这样的前缀和后缀存在
next[0] = j = -1;
//从第1位开始赋值,0位已经是-1了
for (i = 1; i < len; i++) {
//两种情况不需要回溯,一个是前缀的后一位与当前位相等,一个是前面的next值j已经是-1了,表示在此之前没有前缀存在,没有比较下去的意义了
while (!(j == -1 || str[j + 1] == str[i])) j = next[j];//其他情况回溯
//跳出while的原因就两个,先判断是不是当前位和前缀的下一位相同
//相同的话,j表示的是前缀的末位坐标,加个1正好是含当前位的子串的前缀的下标
if (str[j + 1] == str[i]) j++;
//另一种跳出情况是j为-1了,那么前面没有前缀,自己和第一位又不相同,那么包括第i位的子串都没有前缀存在。赋值是-1
//或者判断之后j++了,此时j是含当前位子串的前缀的下标。
//无论怎样,都是当前位的next值
next[i] = j;
}
return;
}
//输入参数为原文,原文长度,目标字符串,目标字符串长度
void KMP(char str[], int slen, char ptr[], int plen){
//创建并计算next数组
int * next = new int[plen];
cal_Next(ptr,next,plen);
//i是原文中的坐标,j是目标字符串的坐标
int i, j;
j = next[0];
for (i = 0; i < slen; i++) {
//如果i和j后面一位不相同,那么回溯
while (!(j == -1 || ptr[j + 1] == str[i])) j = next[j];
//跳出循环的情况有两种
//先分析i与j+1相同的时候,这个时候j移动到下一位
//注意此时j+1不可能超出范围plen,因为全是a的情况最大的j也是len-2,这个是因为下标是从0开始计数的
if (str[i] == ptr[j + 1]) j++;
//如果此时j已经到了plen-1位,即ptr的末尾,那么匹配成功计算并输出头的位置
if (j == plen - 1) {
cout << "位置有:" << i - j << endl;
//开始下一次的查询的初始化
j = next[j];
}
}
return;
}
int main(){
char *str = "bababacaababacad"; //长度为16 (0~15)
char *ptr = "ababaca"; //长度为7 (0~6)
KMP(str, 16, ptr, 7);
system("pause");
return 0;
}