KMP详解

来源:互联网 发布:数据库代理 编辑:程序博客网 时间:2024/06/05 21:11

KMP详解

在讲解KMP算法之前,还是有必要回顾一下BF(简单粗暴但效率相对较低)算法吧

其中S是主串,T是模式串,pos是从什么位置开始匹配

int Inedex(String S,String T,int pos){
int i=pos; //需注意一点无论是主串还是模式串,我们S[0]和T[0]都保存的是字符串长度,而不是字符串本身的字符
int j=1;
while(i<=S[0]&&j<=T[0]){ /*若i小于S的长度且j小于T的长度时循环*/
if(S[i]==T[j]){
i++;
j++;
}else{
i=i-j+2; //这里之所以是i-j+2是因为T[0]不是字符,保存的是长度,你用两个这样的数组比较一下就可以了
}
}
if(j>T[0])
return i-T[0];//找到了,就返回匹配成功的字串的第一个位置。
else
return 0;
}

相信大家看了代码,就知道它的工作原理了吧,即在主串中一个一个与模式串匹配,如果失配,主串的位置就+1,模式串就回到1,这样又重复执行前面的比较,这样的匹配简单易懂,但缺乏技巧。

什么是KMP算法呢?

KMP算法是三位前辈(D.E.Knuth、J.H.Morris、V.R.Pratt)发表的,他们真实太厉害了,向前辈致敬!!!!!

具体表现
第一种情况

S 12 I l o v e d a f a n z i i 0 1 2 3 4 5 6 7 8 9 10 11 12 T 5 I l 0 v x j 0 1 2 3 4 5

明显T中第五个与S中第五个失配了,但你仔细看也会发现,模式串中这个五个不同,所以我们完全可以下一次比较用S中的e和模式串中的I比较。而不必用BF那么累赘。

第二种情况

S 11 w w w . d a f a n z i i 0 1 2 3 4 5 6 7 8 9 10 11 T 3 w w . j 0 1 2 3

明显的T中的第三个和S中的第三个失配了,但这次我们也可以看出下一次匹配中,S中的失配位置(坐标为3的)应该和T串中坐标为2的W开始匹配。

第三种情况

S 12 b b s b b s b b c d f z i 0 1 2 3 4 5 6 7 8 9 10 11 12 T 6 b b s b b c j 0 1 2 3 4 5 6

明显S中的6个位置和T中第6个位置失配了,然而我们也机智的发现S中失配的那个S应该和T中的第三个位置开始匹配了。

第四种情况

S 11 s s s s s s s d f z i i 0 1 2 3 4 5 6 7 8 9 10 11 T 6 s s s s b c j 0 1 2 3 4 5 6

(先声明一下这个表位置摆放没摆好,这里的T表的坐标5个应该对齐S中的坐标8)
可以看出S中的第8个位置和T中的第5个位置失配了,然而我们仔细观察,发现下一次比较其实可以让S中的第8个位置和T中的第4个位置开始匹配了

观察了这么多,其实可以说出KMP的算法的用途了,就是一次匹配失败后让主串失配的那个字符串和模式串中第几个字符和它比较。而不用让主串回溯了,大大提高了效率。而且,其实我们可以总结一些通俗的规律呢?,你比如说第一种情况中,模式串中第5个字符没有被匹配到,而它前面的没有相等的前缀和后缀,所以主串中的失配字符下一次就和模式串中的第一个元素比较。而第二种情况,因为模式串失配的那个字符前有一个前缀和后缀相等,所以呢,主串的失配字符下一次就和模式串的第二个元素比较。第三种情况,前缀和后缀有两个字符,所以主串失配的字符下一次就和模式串的第三个元素开始比较。第四种情况,前缀后后缀有3个元素相等,所以主串失配的字符下次就和模式串的第4个比较。
也许有人问了,第四个怎么就是前缀和后缀相等有4个元素呢?记住:这里的前缀和后缀相等元素的个数是以字符相等情况下,尽可能多的个数,自要前缀和后缀不完全相同就可以了

规律一般化

在这里我们引入next数组,这在KMP算法里是核心,我们就用第三种情况做分析吧。

S 12 b b s b b s b b c d f z i 0 1 2 3 4 5 6 7 8 9 10 11 12 T 6 b b s b b c j 0 1 2 3 4 5 6 next 0 1 2 1 2 3

next数组的功能就是决定模式串中某一个字符失配了,相应下一次失配的那个主串字符就和模式串中的第几个字符匹配(这个模式串的下标就是next数组的值)。这里需要额外说的是,我们默认如果模式串中第一个字符失配,其next[1]是0,这样的意思就是,下次从主串的下一个位置开始匹配了,你画个图,就知道了。

next数组代码原理


void get_next(String T,int *next)
{
int i,j;
i=0;
j=1;
next[1]=0
while(j<[0])
{
if(i==0||[T[i]==T[j])
{
++i;
++j;
next[j]=i;
}else
i=next[i];
}
}

z这一组代码就实现了NEXT数组了

KMP算法改进

先给出一个例子吧:

S 11 a a a a d a f a n z i i 0 1 2 3 4 5 6 7 8 9 10 11 T 3 a a a a a x j 0 1 2 3 4 5 6 j 0 1 2 3 4 5

按照正常的KMP的话,我们会按照给的next数组进行匹配,但是发现d与他们前面四个的匹配都是无效的, 因为d和a匹配失败了,那么d还有必要和前面的a去做比较吗?所以我么可以直接让d和next[1]做比较。这样可以得出改进后的代码

void get_nextval(String T,int *nextval)
{
int i,j;
int i=0;
int j=1
next[1]=0;
while(j<T[0])
{
if(i==0 || T[i]==T[j])
{
i++;
j++;
if(T[i]==T[j])
nextval[j]=nextval[i];
else
nextval[j]=i
}else{
i=T[i];
}
}
}

强烈建议按照上面说的数组,画一次图,推算一次。
然后整体的匹配方法 ,

int Index_KMP1(String S, String T, int pos)
{
int i = pos;
int j = 1;
int next[255];
get_nextval(T, next);
while (i <= S[0] && j <= T[0])
if (j==0 || S[i] == T[j])
++i;
++j;
}
else
j = next[j];
if (j > T[0])
return i-T[0];
else
return 0;
}

因为调用get_nextval(T, next)函数,一个循环为m(m就是模式串的长度),然后while循环执行了n次(n为主串的长度),所以时间复杂度就是m+n吧
转载请注明源地址:http://blog.csdn.net/xiaofanzidafanzi/

0 0