KMP算法又称看 *片算法。 哈哈哈,不要当真了。
看看百度的解释:KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
KMP是三个人的名字首字母。
主要解决的是字符串匹配的问题。
有一个主串 str1 子串str2,要查询str1中是否有str2,如果有返回出现的第一个位置,如果没有返回-1.
正常的做法,我们遍历str1的每个位置,判断接下来是否是str2,
时间复杂度是O(n*m)
要优化,首先,我们肯定要遍历str1,也要遍历str2.
那么不管怎么优化m和n都存在。
KMP算法,优化到了O(M+N)
定义一个概念,对于string中某个字符串,他有一个值,定义为这个字符前面最长前缀和最长后缀的相同匹配长度。
不包括这个字符前面的字符串的整体长度。
很懵逼吧,举个例子。
对于?这个字符串,前面的字符串,aaaaa。
前缀,后缀 为1时是a,相等。
前缀,后缀 为2时是aa,相等。
前缀,后缀 为3时是aaa,相等。
前缀,后缀 为4时是aaaa,相等。
前缀,后缀 为5时是aaaaa,这个时候前缀后缀代表这个字符串肯定相等,无效信息,所以我们取4。
再来一个例子,
这个值是3
对str2每个位置都求一个这个值。
eg:
对0位置求,0位置前面没有东西,设为-1
对1位置求,1位置前面只有1个字符,又不能取1,所以值是0
对2位置求,1
3,0
4,1
5,2
6,3
我们把这个值存在一个next数组里。
[-1,0,1,0,1,2,3]
后面会介绍如何更快的求next数组
不妨设,我们在str1的i位置开始匹配str2.
会发现,最后位置,x和y对不上。这时候暴力方法,我们会放弃i位置。
从i+1位置从新开始。、
KMP算法会从最大前缀后一个位置开始和x进行比对。
这样的原则基于两个前提。
- 最大前缀和最大后缀的字符串相同,也等于str1中对应位置的字符串。
- 在中间的部分不可能出现能构成str2的字符串。
#include <iostream>
#include <string>
using namespace std;
int *getNextArray(string str2){
int *next=new int[str2.size()];
next[0]=-1;
next[1]=0;
int cn=0;//有两个含义,1.代表比对位置。2.代表最大后缀。
int i=2;
while(i < str2.size()){
if(str2[i-1]==str2[cn]){
next[i++]=++cn;
}
else if(cn>0){
cn=next[cn];
//跳到下一个比对的位置
}
else {
next[i++]=0;
//i这个位置,一直都没有找到。cn都跳出循环了,只能是0
}
}
return next;
}
int getIndexOf(string str1,string str2){
if(str1.empty() || str2.empty() || str1.length()<str2.length()) return -1;
int *next=getNextArray(str2);
int i1=0,i2=0;
while(i1<str1.length() && i2<str2.length()){
if(str1[i1]==str2[i2]){
i1++;
i2++;
}
else if (i2==0){
i1++;
}
else{
i2=next[i2];
}
}
return i2==str2.length()? i1-i2 : -1;
}
void test_case1(){
string a="aaaabb";
string b="aab";
cout<<getIndexOf(a,b);
}
int main(){
test_case1();
}