1.KMP算法是什么
- 由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的一种改进的字符串匹配算法KMP算法。关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
- 用于查找串S中含串T的个数count
2.KMP算法的核心
KMP算法是BF算法改进而来的,下边说下BF算法的以及为什么要改成KMP算法
- 一般我们设模式串或匹配串数组元素下标都是从1开始,然后下标0就拿去存放元素个数(为了简便运算),以下边为例来介绍下next数组的获取。
- 下边这个匹配串和模式串在匹配的过程中会发现到第五个元素的时候就不匹配了
- 所以呢就对匹配串右移一个,让匹配串的第一个元素和模式串的第二个元素进行匹配,如果第二个元素匹配就匹配第三个元素,但是恰巧这个匹配串的第一个元素和模式串的第二个元素不匹配,因此直接跳转,让匹配串从模式串的第三个开始匹配,以此类推。
但是 这样的话就会发现每一次都要回溯,重新匹配,那要改进就是要避免这样的回溯使得效率变高,所以KMP算法的核心就是解决问题的核心就是避免不必要的回溯,问题由模式串决定,不是由目标决定。
3.KMP算法的原理
(1)如何避免上边这个回溯?
- 通过观察上边,发现S[i]=T[j](当i和j在1-4间的时候)并且这四个都不相等,所以T[1]和S中前四项都不匹配,由此,我们就可以直接跳转到第和S中的第五项匹配。
- 当匹配数组有重复的时候,那就要匹配的就要考虑重复的,像下边这个
(2)next数组
- 当模式匹配串T失配的时候,next数组对应的元素指导应该用T串的那个元素进行下一轮匹配。
- 一般next数组第一个元素是0,第二个是1(因为前边没有相同的);
- 然后比较数组2 3元素的,发现他们不一样,所以就又向前比较,比较元素1 3,发现他们是一样的,此时就可以记录next的值了,后边的也同理
4.KMP算法代码的原理理解
(1)next数组
typedef char *string;
j是前缀,i是后缀
void get_next(string T,int *next)
{
j=0;
i=1;
next[1]=0;
while(i<T[0]) //当i<模式串元素个数时
{
if(j==0||T[i]==T[j]) //当前缀为0或者前缀对应的元素和后缀对应的元素相同时(或者利用
//j=next[j]让前缀向前)
{
i++; //i和j都加1,因为两个元素相同,所以比较下一个
j++;
next[i]=j; //然后把后缀对应的元素赋予给next数组第i个位置
}
else
{
j=next[j]; //当前缀和后缀不相同的时候,就利用next数组指导j应该
}
}
}
下边是对代码的前5个的运行演示
(2)KMP算法代码的实现
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
typedef char * string;
void get_next(string T,int * next)
{
int j=0;
int i=1;
next[1]=0;
while(i<T[0]) //当i<模式串元素个数时
{
if(j==0||T[i]==T[j]) //当前缀为0或者前缀对应的元素和后缀对应的元素相同时(或者利用
//j=next[j]让前缀向前)
{
i++; //i和j都加1,因为两个元素相同,所以比较下一个
j++;
next[i]=j; //然后把后缀对应的元素赋予给next数组第i个位置
}
else
{
j=next[j]; //当前缀和后缀不相同的时候,就利用next数组指导j应该
}
}
}
//返回字符串T在字符串S中的第pos个字符之后的位置
//若不存在,则返回0
int index_kmp(string S,string T,int pos)
{
int i=pos;
int j=1;
int next[255];
get_next(T,next);
while(i<=S[0]&&j<=T[0])//这个和BF是一样的,就是前边都是匹配的情况
{
if(S[i]==T[j]||j==0)
{
i++;
j++;
}
else //这个就是和BF算法有区别的地方,如果遇到不匹配的,就利用next进行回溯,避免不必要的回溯
{
j=next[j];
}
}
if(j>T[0])
{
return i-T[0]; //如果找得到,就返回i-T[0]的值
}
else
{
return 0; //如果不符合,就返回0
}
}
int main()
{
char str1[255]="ababaaaba";
int next[255];
char str2[255]="bac";
int i,p,m;
//next[0]=next[0]-48;
str1[0]=str1[0]-48;
str2[0]=str2[0]-48;
p=strlen(str1);
get_next(str1,next);
printf("获取的next[]数组为:");
for(i=1;i<p;i++)
{
printf("%d",next[i]);
}
printf("\n");
m=index_kmp(str1,str2,1);
printf("返回的值为:%d\n",m);
return 0;
}