KMP算法
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特–莫里斯–普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。
时间复杂度O(m+n)
KMP算法相当于对BF算法的一种优化,将BF算法中多余的比较省略掉,提高了效率
我们先来看看对比
BF算法具体讲解:https://blog.csdn.net/wfea_lff/article/details/104361318
下图为BF算法图解
下图为KMP算法图解
从上面的两张动图,我们可以看出,BF算法是将P串和S串一个一个比较,直到比出完全相同的串时才停止
而KMP算法则将这一过程优化,将一些没有必要的比较省去,提高了串匹配的效率
那如何省去不必要的比较呢?
其实这里我们做的处理是,将假设P串发生的每一位失配后,S串不动,将P串移动到相应位置上用数组记录下来,当比较的时候如果失配,则直接将P串移动到当前失配所记录的位置,然后继续比较
那如何得到失配后该移动的位置呢
我们举例说明
这里我们会用到next【P的大小】数组(其第一位失配后我们通常规定为-1,其第二位失配后我们通常规定为0,也就是next【0】 = -1;,next【1】 = 0;),通过上面的例子我们很容易得出结论,上图j失配后应该将P串移动成红色箭头之后的样子,很显然k = 2就是j位置失配后P串要移动的位置,即j = next【j】;
最后得出的结论的意思就是:在P串中,存在两个最长相等真子串1,一个以0位置开始,一个以j-1位置结束,k是真子串的长度
我们继续探究next的关系,我们能不能通过已知的next值来得到未知的next值呢?
比如next【j+1】 = func(next【j】),从上面的结论可以看出,k是P串两个最长且相等的真子串的长度,那我如果next【j】 = k;即j位置之前P串两个最长且相等的真子串的长度为k个长度,现在要求next【j+1】,那我就要看看这时的P【j】与P【k】是否相等,如果相等,则关系就为next【j+1】 = k+1;
那如果不等呢??
假如不等,则我们利用KMP算发,就可以看作失配,用失配时处理的方法,将k = next【k】;
这便是KMP算法的思想流程了与BF算法比起来复杂了一些,但效率提高了,算是对BF算法的一种优化
参考代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Getnext(int *next,const char *p,int len)
{
next[0] = -1;
next[1] = 0;
int k,j;
j = 1;
k = 0;
while(j<len-1)
{
if(k == -1||p[j] == p[k])
{
next[j+1] = k+1;
j++;
k++;
}
else
{
k = next[k];
}
}
}
int KMP(const char *s,const char *p,int pos)
{
if(s == NULL||p == NULL)
{
return -1;
}
int slen = strlen(s);
int plen = strlen(p);
if(slen<plen)
{
return -1;
}
int i,j;
i = pos;
j = 0;
int *next = (int *)malloc(sizeof(int)*plen);
if(next == NULL)
{
return -1;
}
Getnext(next,p,plen);
for(int g=0;g<plen;g++)
{
printf("%d ",next[g]);
}
printf("\n");
while(i<slen&&j<plen)
{
if(j == -1||s[i] == p[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
free(next);
if(j>=plen)
{
return i-j;
}
return -1;
}
int main()
{
char *s = "aababcabcdabcdeabcababcabc";
char *p = "abcababcabc";
printf("%d\n",KMP(s,p,0));
return 0;
}