一、KMP是什么?
KMP 算法(Knuth-Morris-Pratt 算法)是一个著名的字符串匹配算法
KMP分别取三个学者名字的首字母
KMP:当字符串出现不匹配的情况下,不再重复进行前一部分已经匹配成功的内容,避免从头匹配。
二、前缀表
前缀表是用来回退使用的
,他记录了模式串和主串发生不匹配的时候,模式串应该从哪里开始重新匹配,应该回退几个。
前缀表:记录下标i之前(包括i)的字符串中,有多大长度的相同前后缀,即最大公共前后缀
。
前缀:指的是不包括最后一个字符的所有以第一个字符开头的连续子串
后缀:指的是不包括第一个字符的所有以最后一个字符结尾的连续子串
最长相等的前后缀的长度(最长公共前后缀):一个字符串中前后缀相等的最大长度
前缀表
使用前缀表进行匹配的过程:
开始匹配时,主串和模式串每个字符进行比对,比较完成后进行向后移动一位,进行比较,遇到冲突不匹配的位置,找模式串该位置前一位的子串的最长相等前后缀,这里的最长相等前后缀就记录在前缀表中,在以上例子中标红的2即为最长相等前后缀,意味着这里的后缀aa前面也有一个与之相等的aa。跳到前缀的后面的一个字符,前缀的后一个字符的下标即为前缀的长度(因为索引从0开始),模式串跳转到前缀后面的一个字符后,继续向后移动匹配,不在进行前面匹配成功字符串匹配。
三、prefix和next数组
prefix存放前缀表 ,next有时会对前缀表进行调整,两者都可以
next数组有些进行右移一位 然后起始位置 “-1”
有些进行将前缀表进行统一减1。
有些也可以直接使用前缀表也可以完成KMP算法。
只是对KMP的不同的实现,不必太过于纠结。
四、代码部分
#include<stdio.h>
#include<string.h>
void get_next(char *T, int next[]) {
int strs_len = strlen(T);
int i = 0, j = -1;
next[0]=-1;
while (i < strs_len) { //遍历
if (j == -1 || T[i] == T[j]) {
i++;
j++;
next[i] = j;
} else {
j=next[j];
}
}
}
int kmp(char *str_main,char * str_branch){
int next[100];
int str_main_len = strlen(str_main);
int str_branch_len = strlen(str_branch);
int i,j;
get_next(str_branch,next);
i=0;
j=0;
/*kmp 核心
当主串i和子串j相等的时候,i++,j++;
反之 j = next[j];
当 j==-1 的时候,i++,j++
*/
while ( i < str_main_len && j < str_branch_len )
{
if(str_main[i]==str_branch[j]){
i++;
j++;
}else{
j = next[j];
if(j==-1){
j++;
i++;
}
}
}
if(j>=str_branch_len){
/*此时说明在主串中找到了子串*/
return i-str_branch_len;
}else{
return -1;//没有找到的话返回-1
}
}
int main() {
char str1[100] = "aabaabaaf";
char str2[100]= "aabaaf";
if(kmp(str1,str2) != -1){
printf("子串在主串中开头位置为:%d\n",kmp(str1,str2));
}else
{
printf("没有在主串中找到该子串");
}
return 0;
}
执行结果:
注:学习笔记,如有错误,还请指正。
感谢各位阅读。