kmp算法在搞懂以后,会觉得求next的过程是非常的美。
//数据结构作业 KMP算法。
//ABAABAAABAAAAB AAAAB(next[])( next_val[]) 1用next[]匹配一遍 2用next_val[]匹配一遍
// A A A A B
// 0 1 2 3 4 坐标
// -1 0 1 2 3 next数组
// -1 0 -1 -1 3 nextVal数组
int* getNext(char* str ,int length){
//length的长度小于1就直接扔掉。
if (length <1){
return new int[1]{-1};
}
//动态声明一个next数组用来存放最长匹配子串
int *next = new int[length];
//人为规定,第一个位置初始化为-1,第二个位置初始化为0
next[0]=-1;
next[1] =0;
//j是从第二个位置开始后的,是next数组的一个位置。
int j = 2;
//k是最大前缀的下一个字符的位置,也代表了前缀的长度
int k = 0;
//这里的while 其实是使用了数学归纳法:假设前一个正确,那么当前值也一定正确。
while (j<length) {
//判断j前一个位置的字符是否和k位置的字符相同,这里的K使用的是最大前缀的下一个字符的位置的含义。
if (str[j-1] == str[k]) {
//如果相等,先让k自加(++k后,k代表了一个新的最大前缀的下一个位置,)
//赋值的含义是 使用了k是最大前缀的字符数
next[j++] = ++k;
}else if(k > 0){
//如果不相等,且K现在还是大于0的
//直接让k跳到字符串的next[k]的位置。进行下一轮while循环。
//这里 可能比较难理解。
//可以看下博客。
//https://www.cnblogs.com/tangzhengyue/p/4315393.html
k = next[k];
}
else{
//此时的k是等于0的;但是不能赋值赋值为next[k],因为next[k]为-1;
//故直接给next的j位置赋值为0;表示没有匹配位置。
next[j++] = 0;
}
}
return next;
}
int* getNext_val(char* str ,int length){
if (length <1){
return new int[1]{-1};
}
int j = 2;
int k = 0;
int *next_val = new int [length];
next_val[0]=-1;
next_val[1] =0;
while (j<length) {
if (str[j-1] == str[k]) {
//kmp 的优化在这里。
//在当前元素的前一个元素 与 k是代表了数学归纳法之后 j前一个元素的最大前缀数。
//k也代表了这个最大前缀的下一个next的位置
//上面判断的是j-1和k相等。这里判断j值k+1
//j-1和k判断的是最大值的下一元素。
//j和k+1判断的是最大值的后面的第二个元素,因为如果后第二个元素相同时,就没有必要比较了。
if (str[j] == str[k+1]) {
//如果是相同的,j位置直接赋值为++k位置的next值。
//这个地方理解的时候一定要往 str[++k]和 str[j] 相等的方向考虑,否则难以理解。
next_val[j++] = next_val[++k];
}
else{
//如果不同,直接赋值为++k的 也就是j-1位置的最长前缀的数+1
next_val[j++] = ++k;
}
}else if(k == 0){
next_val[j++] = 0;
}
else{
k = next_val[k];
}
}
return next_val;
}
/*
s[]为第一个字符串
m[]为第二个字符串
sLength为第一个字符串的长度
mLength为第二个字符串的长度
*/
int getIndexOf(char s[] ,char m[],int sLength,int mLength){
if (mLength < 1 || sLength < 1) {
return -1;
}
int i = 0;
int j = 0;
//拿到next数组
int* next = getNext_val(m,mLength);
while (i < sLength && j< mLength) {
//相等一起移动
if (s[i] == m[j]) {
i++;
j++;
}
//不等,只要返回值大于-1,这说明这两个字符串还能玩下去。直接跳下一个next;如果下一个next为0,m就跳0位置
else if(next[j] > -1){
j = next[j];
}
//不等,但是返回值为-1,说明 m字符串的第一个字符也不能和s匹配了,直接让s跳下一个字符,继续匹配
else if (next[j] == -1){
i++;
}
}
return j == mLength ? i - j: -1;
}
//string类型 转 char类型的字符串指针
char *toCharArray(string oldStr ,int length){
char* newChar = new char[length];
strcpy(newChar, oldStr.c_str());
return newChar;
}
//测试数据
int main() {
string str1 = "ABAABAAABAAAAB";
string str2 = "AAAAB";
int str1Length = (int)str1.length();
int str2Length = (int)str2.length();
char* str1Char = toCharArray(str1, str1Length);
char* str2Char = toCharArray(str2, str2Length);
int index = getIndexOf(str1Char, str2Char, str1Length, str2Length);
cout<< index<<endl;
return 0;
}