MP算法介绍:
字符串匹配中,如果我们选择最朴素的BF算法,即每次匹配失败就返回到该次刚开始匹配的位置,时间复杂度为O(M*N),M,N分别为匹配字符串p和被匹配字符串s的长度。这个复杂度在M和N长度都很大时是极其不方便的,所以,我们需要想办法利用现有的资料去减少匹配次数。如果我们在p[i]处匹配失败了,可以知道的是前0-i-1是匹配成功的,那么我们可以根据这个将P向后滑动,而不是将P移到最前面。
MP算法实现原理
现在我们需要知道的是向后滑动几个字符。我们怎么判断向后滑动的字符数呢?当然是滑动到其当前后缀与前缀相同的最大位置,例如abcabcc为匹配字符串,如果在第6位时失配了,那我们可以知道的是前5位是相同的,那么ab这个后缀与前缀ab是相同的,我们就可以把字符串滑动到第三位继续比较,从而节省了大量的时间。
预处理mpNext数组
void preMp(char s[]){
int length=strlen(s),i=0,j=-1;
mpNext[0]=-1;
while(i<length){
while(j>-1&&s[i]!=s[j])j=mpNext[j];
mpNext[++i]=++j;
}
for(i=0;i<=length;i++){
printf("%d ",mpNext[i]);
}
printf("\n");
}
KMP算法实现原理
KMP是在MP算法的基础上改进出来的,核心思想为避免MP算法中一次明显失败的匹配。
例如abcabcc为匹配字符串,如果在第6位时失配了,那我们可以知道的是前5位是相同的,那么ab这个后缀与前缀ab是相同的,MP算法会将字符串移动到第3位,但是第3位也是c,而第6位的字符可以确定不是c,所以就在代码中添加条件,如果mpNext数组第i位匹配的位置j的字符a和第i位字符相同,则kmpNext数组中会赋值为kmpNext[j],而不是j。
预处理KMP数组
void preKMp(char s[]){
int length=strlen(s),i=0,j=-1;
KmpNext[0]=-1;
while(i<length){
while(j>-1&&s[i]!=s[j])j=KmpNext[j];
++i;
++j;
if(s[i]==s[j])KmpNext[i]=kmpNext[j];
else KmpNext[i]=j;
}
for(i=0;i<=length;i++){
printf("%d ",mpNext[i]);
}
printf("\n");
}
MP算法测试代码
KMP就是将其替换为上面的方法即可,注意数组名。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int mpNext[100];
void preMp(char s[]){//预处理mpNext数组
int length=strlen(s),i=0,j=-1;
mpNext[0]=-1;
while(i<length){
while(j>-1&&s[i]!=s[j])j=mpNext[j];
mpNext[++i]=++j;
}
printf("预处理mpNext数组:\n");
for(i=0;i<=length;i++){
printf("%d ",mpNext[i]);
}
printf("\n");
}
int main() {
char S[]="catcatcaatcatcatcaatcatacaat";
char P[]="catcaat";
printf("被匹配字符串:\n%s\n",S);
printf("匹配字符串:\n%s\n",P);
preMp(P);//预处理mpNext数组
int i=0,j=0,sl=strlen(S),pl=strlen(P);
while(i<sl){
while(j>-1&&P[j]!=S[i])j=mpNext[j];
i++;j++;
if(j>=pl){
printf("Matching at: %d\n",i-j+1);
j=mpNext[j];
}
}
return 0;
}