一、KMP算法与Brute-force算法的最大区别就在于KMP算法在比较时,主串的指针i不用回溯,只需回溯字串的指针j
而Brute-force算法就是简单的从第一个字符比到最后一位,这时需要对主串的i进行回溯,这样算法的比较次数明显增多
KMP的算法复杂度可为O(m +n)而Brute-force的算法复杂度可为O(m*n)。
二、在做KMP时我们可以知道每一次字串j的回溯,即每一次字串后移的位数等于匹配的位数减去部分匹配的位数
即可以得到一公式 移位数 = 匹配位数 - 部分匹配位数
如何得到部分匹配位数呢?
这是由字串的前缀与后缀的最大共同的字符串决定的。如ABDAB这是部分匹配位数为2
三、如何推算next[]呢?
由特殊到一般:
假设:有next[j]= k,即“t[0]...............t[k-1]”=''t[j-k]................t[j-1]'',0<k<j,此时next[j+1]有两种情况
字串:
t = t[0].........t[j-k]......t[j-1].......t[j]......t[m-1]
t' = t[0].......t[k-1] t[k]
t' =t[0]....................t[k']
if(t[k] == t[j]) {
next[j+1] = next[j] +1 = next[k] +1;
}
//此时我们就需从字串t'中找到t' 等于t[k]的情况
else if(t[k ] != t[j]){
next[j+1] = k'+1= next[k]+1;
}
//如果没找到则k = 0
else{
next[j+1] = 0;
}
//以上代码只是分析使用的是伪码。
KMP代码:
/**
*KMP 字符串匹配算法核心是GetNext[]这一步,要知道在j>1时不匹配应该保证i不变,获取next[j]的值
*由字符串的比较我们可以知道我们可以保证i不变来做比较,i是不用回溯的只需回溯j的值就 可以了
*/
#include <iostream>
#include <windows.h>
#include <string.h>
using namespace std;
typedef struct{
char str[100];
int length ;
}KMPString;
/**
*字符串的比较
*@param S,start ,T,next分别表示主串,开始比较的位置,字串以及next[j]的值
*@return 返回成功匹配后的v值表示成功的第一次出现的下标,如果匹配失败返回-1;
*/
int KMPIndex(KMPString S,int start,KMPString T,int next[]){
cout<<"字串:"<<endl;
for(int i = 0;i <T.length;i++){
cout<<T.str[i];
}
cout<<endl;
cout<<"主串:"<<endl;
for(int i = 0;i <S.length;i++){
cout<<S.str[i];
}
cout<<endl;
int i = start;
int j = 0;
int count = 0;
int num = 0;
int v ;
while(i <S.length && j <T.length){
if(S.str[i] == T.str[j]){
count++;
i++;
j++;
}
else if(j == 0){
i++;
}else{
num++;
j = next[j];
}
}
if(j == T.length){
cout<<"匹配成功!"<<endl;
v = i-T.length;
}else{
cout<<"匹配失败!"<<endl;
v = -1;
}
cout <<"count="<<count<<" "<<"num = " <<num<<endl;
return v;
}
//获取next[j]
void GetNext(KMPString T,int next[]){
// cout<<T<<endl;
int j = 1;
int k = 0;
next[0] = 0;
next[1] = 0;
cout<<"T长度:"<<T.length <<endl;
for(int i = 0;i <T.length;i++){
cout<<T.str[i]<<endl;
}
while(j < T.length){
cout<<"第"<<j<<"次进入while循环"<<endl;
if(T.str[j] == T.str[k]){
next[j+1] = k+1;
j++;
k++;
}else if(k == 0){//这里是个出口,在循环后没有找到真串时,下一步跳回第一步也就是j 回溯到0
cout<<"进入k ==0"<<endl;
next[j+1] = 0;
j++;
}else{//这里可以不断的获取到真串的存在,如果存在next[j]下一步调到j = k
cout<<" k = next[k]"<<endl;
k = next[k];
}
}
for(int i = 0;i <T.length;i++){
cout<<"next["<<i<<"]="<<next[i]<<endl;
}
cout<<"k="<<k<<endl;
}
//main函数
int main(){
KMPString S = {{"aaaaaaaab"},9},T = {{"aaaab"},5};
//KMPString S = {{"aaaaaaaa"},8},T = {{"aaaab"},5};
int next[100] ={0,1,2,3};
int count = 0 ;
int pos =0;
cout<<"获取GetNext[j]"<<endl;
GetNext(T,next);
cout<<"获取next成功!"<<endl;
pos = KMPIndex(S,0,T,next);
if(pos >= 0){
cout<<"获取到匹配成功的位置!"<<endl;
cout<<"匹配成功的位置:"<<pos<<endl;
for(int i = pos ;i <S.length;i++){
cout<<S.str[i];
}
cout<<endl;
}
system("PAUSE");
return 0;
}