算法思想:
KMP算法实现寻找主串中子串的位置时,主串指针地址不回退,在比对过程中串仅仅遍历一次,子串的回退可以是与当前主串可重新最多匹配的地址位置。
BF与KMP算法比对:
KMP | BF |
主串不用回退 | 主串回退,回退到此次开始比较的下一位置 |
子串的回退可以是与当前主串可重新最多匹配的地址位置 | 子串回退到开始位置 |
思路展示:
1、首先遍历主串中与子串一致的字符,直至出现无法匹配:
如图:主串与子串的粉色阶段相同,而后面的无法匹配
2、当我们发现,在主串与子串相同的部分内,主串后一部分与子串前一部分相同
如图:主串与子串紫色部分相同
可以设想一下,子串是否可以从紫色结尾的地方重新和主串的当前位置再次做匹配
3、由步骤一则知道,主串与子串粉色部分相同,步骤二引入紫色部分相同,则可以推导出:
推导出:子串的前一部分与子串的后一部分相等
4、由思路推出表达式:
- 设子串此次匹配的结束地址下标位置为 j;
- 开始下标:0;
- 紫色结束下标:k-1;
- 由紫色部分长度、字符都相等,所以长度: k-1-0=j-1-紫色开始下标;
- 所以紫色开始下标:j-k;
结论:
匹配成功的子串片段中,寻找两个最长且相等的真子串,真子串长度决定子串返回的地址下标
真子串特点:
- 一真子串以子串的开头为开头
- 另一真子串以子串匹配片段的结尾为结尾
- K为子串的长度,也是子串在匹配失败时需要返回的位置
next数组及其练习(重点)
我们由上一个结论所得的K 值,保存得到的数组(next数组)
固定值:
next[0]=-1; next[1]=0;
易错:
1、子串的截断字符不包含在此次子串片段范围内,当前的截断字符的next数组值,由截断前的所有字符作为判断决定
请判断:
红色为标准错误,绿色为正确答案
2、子串的真子串比对容易漏掉最长真子串
红色为标准错误,绿色为正确答案
练习例子:
1:"abacdabcabaae"
-1001001201231
2:"abacdabcabcde"
-1001001201200
寻找next[]的固定规律:
- 当我们已知下标为i 的next数组的值时:next[i]=k;
- 需要判断子串str[]:str[k] 与 str[i] 是否相等?
- 相等: next[++i]=++k;
- 不相等:
此时问题则迭代成为在当前已经遍历的字符串中寻找最长的相等真子串
->求当前 str[i+1] 对应的 next数组值
->即为 next[k]对应的next数组值
-> next[++i]=next[k]
KMP算法的实现:
#include <iostream>
#include <string>
#include<assert.h>
using namespace std;
//KMP 算法,i 不回退
//思路:
//下标: j-k ...j-1 = 0...k
//找子串的两个最长真子串!首位两个真子串
//真子串长度为k;(next 数组)
//!注意,当前位置为截断不等位置,此位置的前一个位置为尾
int BF(const char* str1, const char* str2, int pos)
{
assert(str1 != NULL && str2 != NULL);
int len1 = strlen(str1);
int len2 = strlen(str2);
int* next = (int*)malloc(sizeof(int) * len1);
next[0] = -1;
next[1] = 0;
int i = 1;
int k = 0;
while (i+1 < len1)
{
if ((k == -1) || str1[i] == str1[k])
next[++i]= ++k;
else
k=next[k];
}
if (pos < 0||pos>=strlen(str1)||str1==NULL||str2==NULL)
return -1;
int j = 0;
i = pos;
while(i<len1&&j<len2)
{
if ((j==-1)||(str1[i] == str2[j]))
{
i++;
j++;
}
else
{
//KMP 算法:i保留在当前位置,与j 再次比较
j = next[j];
/*BF 算法:j = 0; i = i - j + 1;*/
}
}
if (j == len2)
return i - j;
return -1;
}
int main()
{
const char *str1 = "ababcabcdabcdeabcdabcdd";
//const char* str1 = "abcd";
const char* str2 = "abcd";
int i = 0;
int j =0;
j = BF(str1, str2, j);
while(j!=-1)
{
j = BF(str1, str2, j);
printf("返回类比pos位置:%d\n", j);
if (j == -1)
break;
j += strlen(str2);
}
return 0;
}
时间复杂度:
- KMP:O(m+n);主串只遍历一次
- BF: O(m*n)