基本数据结构与算法笔记之--KMP算法
注:用于自己复习之用
KMP算法介绍:
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
在KMP算法中关键就是求解next数组的值,故此笔记主要记录next的求解过程
1.未优化的KMP版本
next[i]指的是在串p[0]~p[i-1]中最大相同的前后缀: 比如:p[0]~p[i-1]是 "aaa****aaa"(****指的是不相同的乱字符)它的前后缀为: "aaa"
/*
next数组是这样的:
next[i]指的是在模式串p中的index 0~i-1中最长的相同的前缀和后缀长度
*/
int *get_next(char *p){ //获取模式串p的next数组
int p_len = strlen(p), i, k = -1;
if(p_len == 0)
return NULL;
int *next = (int *)malloc(sizeof(int) * p_len);
next[0] = -1; //初始next[0] = -1
for(i = 1; i < p_len; i++){
while(k != -1 && p[k] != p[i-1])
k = next[k]; //回溯
k++;
next[i] = k;
}
return next;
}
2.优化的KMP版本
优化示例:如在 p[0]~p[i]:"aaab****aaab"在未优化版本中next[i]==3,在实际中,可优化该值,在i位置出现了失配即T[j-i]到T[j-1]与p[0]到p[i-1]相配而在T[j]与p[i]失配。按未优化的则j-=next[i]
//优化后的next
int *get_newnext(char *p){
int p_len = strlen(p), i, k = -1;
if(p_len == 0)
return NULL;
int *next = (int *)malloc(sizeof(int) * p_len);
next[0] = -1; //初始next[0] = -1
for(i = 1; i < p_len; i++){
while(k != -1 && p[k] != p[i-1])
k = next[k]; //回溯
k++;
if(p[i] == p[k])
next[i] = next[k];
else
next[i] = k;
}
return next;
}
int kmp(char *T, char *p){ //主字符串T, 模式字符串p
int T_len = strlen(T), p_len = strlen(p), i = 0, j = 0;
int *next = get_newnext(p);
while(i < T_len && j < p_len){ //i不回溯,j回溯
if(j == -1 || T[i] == p[j])
{
i++;
j++;
}
else
j = next[j];
}
if(j == p_len)
return i - p_len;
return -1;
}
void KMP(char *T, char *p){
int i = 0, t;
while(1){
t = kmp(&T[i], p);
if(t == -1)
return;
i += t + 1;
printf("在index=%d处匹配成功!\n", i);
}
}
void dispP_Str(char *p){
int p_len = strlen(p);
int i;
printf("打印模式串p如下:\n");
for(i = 0; i < p_len; i++)
printf("%c ", p[i]);
printf("\n");
}
void dispNext(int *next, int len){
int i;
printf("打印next数组如下:\n");
for(i = 0; i < len; i++)
printf("%d ", next[i]);
printf("\n");
}