首先是部分入门解释:
1:求next数组
当我们假设 模式串patten 为 aaabc时,
a a a b c
对应的 NEXT数组为:
-1 0 1 2 0。
Next 数组的含义:
求next数组的时候,对于模式串的 J 位置 ,考察patten[ 0 ].到patten[j-1]组成的字符串 最长且相等的前缀和后缀。
假设最大相等前缀和后缀长度为k,则有k使得 p[0]p[1]p[2]......p[k-2]p[k-1] = p[j-k]p[j-k+1]......p[j-2]p[j-1]。
先理解 前缀 和 后缀,例 "aaabc" 字符串的前缀,后缀各有4种,相互独立的。
前缀 后缀
a a a b c
a a a b c
a a a b c
a a a b c
前缀不包含 最后一个字符,
后缀不包含 第一个字符。
patten[0]前无字符,亦无后缀,所以next[0]=-1;表示不存在。
patten[1],在patten[0]~patten[0]找前后缀,均为空,next[1]=0;
patten[2],最长相等前缀 'a',后缀为 'a',next[2]=1;
patten[3],最长相等前后缀为 ‘aa’和‘aa’ ( 即前缀 patten[0]patten[1],后缀 patten[1]patten[2])next[3]=2;
patten[4],后缀中含有 patten[3]=b,前缀没有 ‘b’,next[4]=0;
求该数组过程如下:
算法解释如下:(关于回溯过程的解释,说有些啰嗦,可以自己求一下,我的过程在最下面的贴图上)
2 :KMP解释
在BF暴力算法中,假如从文本串的第 i 个字符来开始于模式串匹配。当匹配到模式串的第j位发现失配
即text[i+j] != patten[j]的时候,我们又从文本串的第i+1个位置来重新开始匹配。
可以看到每次失配之后,我们需要从文本串的下一个位置[ i+1 ]匹配,模式串从第一个字符重新开始于文本串匹配。并且在已经知道很多字符都配不上的情况下,还要这样一个一个字符移动着去匹配,是非常浪费时间的。
假设文本串长为 n,模式串长为 m,BF暴力穷举方法时间复杂度O(n*m),
而KMP算法的实质就是,当遇到text[i+j] != patten[j]的时候,但是我们知道模式串中的 0~j-1 位置上的字符已经于i ~ i+j-1位置上的字符是完全匹配的。这样我们可以在 0 ~ (j-1) 中找到一个前缀A和后缀B相等并且最长的那个串,然后将A移动到B的位置再开始重新匹配即可。
这样就减少了一些不必要的匹配。时间复杂度O(n+m)
借用其他博主的图片和字符串
准备好next数组后开始kmp匹配,开始匹配位置 i=0,可以看到,前面的红色黄色处均匹配,在蓝色标(J=5)记处不匹配,蓝色处字符对应的next数组值为 2 ,说明 蓝色字符C前面最长相等的前后缀为 a b,长度为2,因为红色格子表示前后缀相等,所以把模式串划过去,直接让 黄色格子 a 直接对准 上次蓝色格子 C对应的文本串位置
模式串滑过去后如图,这样就避免了BF算法中的 i=0匹配失败,继续匹配不可能匹配的 i=1,i=2两个位置,恰好移动后,模式串和文本串匹配,假设模式串和文本串不匹配,模式串就继续滑动。
代码如下
/* KMP算法
未改进的
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1000; //假设最长字符串长度
char text[maxn]; //文本串(目标串 被检测)
char patten[maxn]; //模式串
int next[maxn]; //模式串的 next数组
/*
*/
void GetNext(){ //获取 next数组
int Len_p=strlen(patten);
next[0]=-1;
int k=-1,j=0;
while(j<Len_p){
if(k == -1 || patten[j] == patten[k]){
++k; ++j;
next[j]=k;
}else{
k=next[k];
}
}
}
int KMP(){ //KMP算法
int Addr=-1,i=0,j=0;
int Len_p=strlen(patten),Len_t=strlen(text);
while(i<Len_t){
if(j == -1 || text[i] == patten[j]){
++i; ++j;
}else{
j=next[j];
}
if(j == Len_p){
return i-Len_p;
break;
}
}
return Addr;
}
int main(){
scanf("%s%s",patten,text);
GetNext();
for(int i=0;i<strlen(patten);i++){
printf("%d ",next[i]);
} printf("\n");
printf("%d",KMP());
return 0;
}
运行如下:
第一行输入 模式串 asd,第二行输入文本串 asdf,第一行结果是模式串的next数组,第二行为模式串第一次出现在文本串的位置
求模式串“aaabc”的next数组的算法每一步
引用参考https://blog.csdn.net/suguoliang/article/details/77460455#commentBox
博主:黑脉金