未经博主许可,禁止转载!
之前看过网上很多KMP的讲解,但是很难受的是,太长而且冗杂,在看本博客前,我先推荐看完本视频https://m.bilibili.com/video/av3246487.html?from=search&seid=17838694507966022690看完之后相信能解决很大一部分问题
======================================================================
KMP在查找子串相较于BF算法来说好在他对于重复部分的处理上。普通的BF每次在查找一次子串之后,只能向下移一位,而KMP的失配函数可以处理出可跳跃的部分。
KMP原理:
·以字符串S = abcdabbkm, T = abcdabc为例.
·我们可以看出来一直匹配到子串abcdab之前是相同的,且该子串的前缀和后缀是相同,如果按照BF算法那么下一步就是从第二个位置(b)开始找,但是我们发现,前后缀相同,我们只是在abcdabc的最后一个位置(c)没匹配成功,那我们可不可以直接比较S[6]= 'b' 和T[2] = 'c' 进行比较呢?思考一下。
·是可以的,因为在abcdab这个串中我们已经知道了ab能再次出现的位置是后两位,从cd的位置再比较是完全没意义的。
那么我们怎么能知道究竟一个串的模式值(前缀后缀相同的长度)是多少呢?这里引入失配函数。
失配函数(next)原理:
·模式值初始设置为-1,表示该前缀串中不存在前缀后缀相同的部分(有的也设置为0,效果一致)
·以两个指针i,j分别进行移动。i指向本串现在的起始位置,j指向当前前缀串中前缀和后缀相同部分的长度。
·如果i和j所指元素相同,那么模式值+1,并将i,j向后移位。
·若不相同,我们需要从之前已经扫描过的模式串中找出尽量长的前后缀相同的子串
因此我们不难写出代码:
int next[len];
void getFail(char* T) {
int len = strlen(T);
int k = -1;
next[0] = -1; //next[0]初始化为-1,-1表示不存在相同的最大前缀和最大后缀
for (int i = 0; i < len; i++) {
while(k > -1 && T[i] != T[k+1]) {
k = next[k];
}
if (T[k+1] == T[i]) { //若i和j所指向元素相同,则++
k ++;
}
next[i] = k; // 将得到的模式值赋给next数组
}
}
相对的我们可以写出KMP算法了:
int KMP(char* S, char* T) {
int len_s = strlen(S);
int len_t = strlen(T);
int k = -1;
for (int i = 0; i < len_s; i++){
while(k > -1 && S[i] != T[k+1]) { //查找当前位置及之前串中前后缀相同的最长长度
k = next1[k];
}
if (T[k+1] == S[i]) { //如果相同那么k++
k ++;
}
if (k == len_t -1) {
return i - len + 1;
// cout << i - len_t + 1 << "+++" << endl;
// i = i -len + 1 ;
// k = -1;
// 注释部分是可以找到全部位置的代码
}
}
}
另一种写法,你可以将k赋值为0,只不过需要将代码中的k+1部分改为k即可