KMP algorithm - six steps to get KMP
1. What is KMP
KMP algorithm is DEKnuth, J, H, Morris and co-sponsored VRPratt three chiefs, called Knuth-Morria-Pratt algorithm, referred to as KMP template matching algorithm. The algorithm with respect Brute-Force (violence) algorithm has a relatively large improvement, mainly eliminating the main string pointer back to improve time efficiency. (Space for time)
2.KMP and simple template matching (Brute-Force)
Violence Brute-Force
This comparison is super simple stepping through. . . .
Given a string S, T ,
a T to S first one of length T as the comparison string, returns a successful first index matching
is unsuccessful, continues to match the S next of length T the same word string
...... through each repeat length of the T same string, and matching
this time complexity is O (n * m).
KMP template matching algorithm - can implementation complexity O (m + n)
The first step: to understand what strings are prefixes and suffixes
such as:
abcjkdabc, then the longest prefix of the array and the longest suffix same - must be abc.
cbcbc, the same longest prefix and suffix longest - is cbc.
abcbc, longest prefix and suffix of the same maximum, it does not exist.
Note longest prefix: the first character is said to start, but do not include the last character.
第二步,理解关于字符串匹配的规则
假设S与T要进行匹配,匹配规则如图
这里aba就是最长前缀字串,然后下一次a是最长前缀字串
借别人的图示意:
第三步,如何得出j的更新规则——即计算next数组
我看了数据结构(java)第四版,讲的很好。
公式:
例子演示:
第四步,上计算next数组的代码
int get_next(string t,int * next) {
//先定义一个数组
int length = t.length();
int j=0,k=-1;
next[0] = -1;
while(j<length-1)//因为我们是在找最长前缀字串,这里得减一。
if(k==-1||t[j]==t[k]) { //当前的字串最新的字符相等或者字串总长度1
k++;
j++;//比较下一个字符
next[j] = k;//最长前缀字串记录下来,因为前面的k,j都加了1
} else
k = next[k];//重新在k长度的前缀字串找当前j长度字串的最长前缀字串,更新索,听说这里是最难理解的
return 1;
}
第五步,理解 k = next[k]
k = next[k];//重新在k长度的前缀字串找当前j长度字串的最长前缀字串,
这里我给例子演示,反正我是一下子就理解了。
图示:因为p5!=p11,这个时候,最长前缀字串肯定不会比上次的大了,不可能是6,我们只能重新寻找,而重新寻找的规则就是k = next[k]=2
第六步,最后一步,上匹配字串的代码
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
//建立next数组
int get_next(string t,int * next) {
//先定义一个数组
int length = t.length();
int j=0,k=-1;
next[0] = -1;//这里是为了
while(j<length-1)//因为我们是在找最长前缀字串,这里得减一。
if(k==-1||t[j]==t[k]) { //当前的字串最新的字符相等或者字串总长度1
k++;
j++;//比较下一个字符
next[j] = k;//最长前缀字串记录下来
} else
k = next[k];//重新在k长度的前缀字串找当前j长度字串的最长前缀字串,更新索
return 1;
}
int compare_str(string s,string t,int pos) {
int n=s.length();
int m = t.length();
if(n<m||pos>=n||n==0) { //不合理的输入
return 0;
}
if(pos<=0) {
pos=0;//提高容错率
}
int next[m];
//生成next数组
get_next(t,next);
int i=pos,j=0;
while(i<n&&j<m)
if(j==-1||s[i]==t[j]) { //当前匹配成功,继续匹配下一个字符
i++;
j++;
} else { //i是不回溯,匹配规则更新匹配的j
j = next[j];//这里就是我们说的匹配规则
if(n-i+1<m-j+1)//剩下的字串不够t的长度,就不用比较了
break;
}
if(j==m)
return i-j;//成功匹配。,返回对应的索引
return -1;//否则退出
}
int main() {
string t,s;
cin>>s;
cin>>t;
int pos =compare_str(s,t,0);
cout<<pos;
return 0;
}
更新改进KMP算法。
现在我们比较的是C和B,不匹配。
显然,当我们上边的算法得到的next数组应该是[ -1,0,0,1 ]
所以next[3] = 1。下一步的匹配:明显我们又来比较C和B,这是是多余的
所以我们应该改进计算next数组匹配
改进的算法 就是避免这种情况,代码演示
int get_next(string t,int * next) {
//先定义一个数组
int length = t.length();
int j=0,k=-1;
next[0] = -1;
while(j<length-1)//因为我们是在找最长前缀字串,这里得减一。
if(k==-1||t[j]==t[k]) { //当前的字串最新的字符相等或者字串总长度1
k++;
j++;//比较下一个字符
//使用改进的算法
if(t[j]!=t[k])
next[j] = k;//最长前缀字串记录下来
else next[j] = next[k];//直接跳到上次找到的后缀字串,改进的代码结束
} else
k = next[k];//重新在next[k]长度的前缀字串找当前j长度字串的最长前缀字串,更新索引
return 1;
}
Reference blog
https://blog.csdn.net/dark_cy/article/details/88698736 :
https://blog.csdn.net/starstar1992/article/details/54913261?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source= distribute.pc_relevant.none-task