KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
很明显,我们可以看出这个算法时间复杂度很低,如果用传统方式,那么时间复杂度为n2,这有时候字符串太长,效率就很低。
KMP算法的核心在于,如何创建next数组,在此我简单说下:
如abcabcb这个pattern字符串。
我们需要寻找前缀与后缀
初始next[0] = -1;表示next数组头
1. a 前缀{空},后缀{空},next[1]=0
2. ab 前缀{a},后缀{b} ,next[2]=0
3. abc 前缀{a,ab},后缀{c,bc} ,next[3]=0
4. abca 前缀{a,ab,abc} 后缀 {a,ca,bca} next[4]=1 此时公共前后缀长度为1
5. abcab 前缀{a,ab,abc,abca} 后缀**{b,ab,cab,bcab}**next[5]=2 此时公共前后缀长度为2
6. abcabc 前缀{a,ab,abc,abca,abcab} 后缀{c,bc,abc,cabc,bcabc} next[6]=3 此时公共前后缀长度为3
分析:通过当前角标前一个的子串前后缀个数+1,即为当前next数组的值 next[i+1] = j+1 (j为前一个子串的前后缀个数)即用i推出i+1的next[i+1]的值.
所以next数组为{-1,0,0,1,2,3}
具体代码如何实现:
#include<iostream>
#include<string>
#include<stdlib.h>
using namespace std;
int next[100];//存储next数组
void getNext(string s1){
next[0] = -1;//和严蔚敏老师的略不同,我从下标0开始,严蔚敏老师从1开始
int i = 0,j = -1;
while(i<s1.length()){
if(j==-1||s1[i]==s1[j])
next[++i] = ++j;//++i处的next值为之前子串中,前后缀匹配个数+1 即next[i+1] = j+1;因为i和j要自增,所以写成++i和++j
else
j = next[j];//如果不匹配,那么j的值进行前移,寻找与i处匹配的字符,如果都不匹配,则到j=-1为止。
}
for(int i = 0;i<s1.length();i++)
cout<<next[i]<<" ";
}
void KMP(string s1,string s2){//左为待匹配串,右为模式串
int i = 0,j = -1;
while(i<s1.length()){
if(j==-1||s1[i]==s2[j]){
i++;j++;
}
else{
j = next[j];//i始终大于j
}
if(j==s2.length()){
//匹配结束
cout<<"第"<<i-j+1<<"个字符位置处开始匹配"<<endl;
i = i-j+1;//从当前匹配为止的下一个字符开始,再进行匹配,即aaaa中有3个匹配aa的
j = -1;
}
}
}
int main(){
string s1 = "abcdeabcdssaaabcabcabcabdxxcvxzvadf";//待匹配串
string s2 = "abcabcb";//模式串
getNext(s2);
KMP(s1,s2);
return 0;
}
之前写的KMP是错误的,后来学习数据结构在发现学了一个错误的算法、在此抱歉