KMP算法
简介
KMP 算法是 D.E.Knuth、J,H,Morris 和 V.R.Pratt 三位神人共同提出的,称之为 Knuth-Morria-Pratt 算法,简称 KMP 算法。该算法相对于 Brute-Force(暴力)算法有比较大的改进,主要是消除了主串指针的回溯,从而使算法效率有了某种程度的提高。(网上抄的)
算法理解与实现
Problem:
KMP算法是用于解决字符串的模式匹配问题,通俗来说就是在字符串s1中寻找s2并将其定位。
Algorithm:
在最初的BF(Brute-Force)算法也就是最容易想到的暴力算法中,最坏的时间复杂度达到O(n*m),在一些时间复杂度有要求的题中是接受不了的,而KMP的时间复杂度达到了O(n+m),也就是说经过处理后可以不用再次从头进行匹配。但是KMP也用一定的局限性即只能处理一对一的匹配问题,多模式匹配详见AC自动机~。
在我个人的理解中,KMP算法的核心是通过匹配该字符前的”操作记录“来得到对于本次操作的有利信息,即该字符失配后应该用前面的哪个字符与该字符再次进行匹配(也可以看作将字串平移),此时该字符前的字符一定是成功与字串匹配的(不理解先跳过这句话),减少不必要的回溯,大大减少了时间复杂度。
next数组就充当了记录“有利信息”的角色。
我自己对于next数组的定义:next[i]是记录s[0]到s[i-1]的前缀和后缀相同的最大位数(如s=“abcdabce”,s[7]=‘e’,next[7]=3,因为s[0]到s[i-1]的前缀s[0]s[1]s[2]="abc"和后缀s[4]s[5]s[6]="abc"是相同的且位数为3),因为s串是从0开始所以s[3]也正好指向最大相同前缀的后一位。
我将next数组的计算和字符串匹配的过程放在了一张图里。
如下:
在图中会发现,在该字符失配后与next数组所指向的字符进行比对,他们前面的字符总是相同的,无论是在求next数组的过程中和进行模式匹配的过程中都有体现,这就是next数组的魅力,在代码中的体现是在语句"j=next[j]"。
代码:
获取next数组
void Getnext(int next[],String t)
{
int i=0,j=-1;
next[0]=-1;
while(i<t.length-1)
{
if(j == -1 || t[i] == t[j])//j等于-1说明该字符和s[0]也匹配失败,则直接到下一个字符的匹配环节
{
i++;j++;
next[i] = j;
}
else j = next[j];//点睛之笔!!!
}
}
拿到next数组的值后就可以进行KMP匹配啦!
int KMP(String s,String t,int next[])
{
while(i<s.length&&j<t.length)
{
if(j==-1 || s[i]==t[j])
{
i++;
j++;
}
else j=next[j]; //和求next数组有着异曲同工之妙
}
if(j>=t.length)
return (i-t.length);
else
return (-1);
}
看懂这里以后还会出现一个遗留的问题,在严老的数据结构中写道:
为了让这个问题的得到解决只需要在求next数组的语句中加上一句判断:
void Getnext(int next[],String t)
{
int j=0,k=-1;
next[0]=-1;
while(j<t.length-1)
{
if(k == -1 || t[j] == t[k])
{
j++;k++;
if(t[j]==t[k])//就是这个!!
next[j] = next[k];
else
next[j] = k;
}
else k = next[k];
}
}
我看过一句话叫做“你能把别人教会你就是真正的会”,前天熬到一点多和KMP斗智斗勇所以我决定把我所理解的写下来,我的室友也都在打呼噜了,明天再让他们学KMP,看我的文章直到学会为止(^ - ^)!这样我也就真正的会了嘿嘿,自从退役以后也没写过博客,正值考研阶段我也决定重拾这份工作了,期待我的下次更新~
撒花~