KMP
什么是kmp?kmp是字符串的一种匹配算法。
在学kmp之前我们可以来看一种易于想到的方法,我们叫它“朴素的匹配算法”。
例如:在abcdefg这一字符串中,def这一字符串在哪一个位置?
朴素的算法就是一个一个匹配,直到全部匹配完。我们可以自定义一个函数用来求该位置的下标。如下
int Index(char s[],char t[])//s[]代表上述abcdef,t[]代表上述def
{
int i=1,j=1;//初始化
while(i<strlen(s)&&j<strlen(t))
{
if(s[i]==t[j])//两字符相等则继续
{
++i;
++j;
}
else
{
i=i-j+2;
j=1;//从头开始
}
}
if(j>strlen(t))
return i-strlen(t);
else
return 0;
}
我们很容易就发现这种算法很浪费时间,所以kmp就出现了。
kmp算法中最最最重要的就是如何求next[]数组,所以要想了解kmp,我们必须得学会求next[]数组。
那么什么是next[]数组呢?
next[]数组就是用来存放某字符串中某个字符之前的一段字符串的前缀和后缀相同的长度。
那么我们如何理解呢?
举个例子:
abcdex
它的
前缀:a,ab,abc,abcd,abcde,abcdex;
后缀:x,ex,dex,cdex,bxdex,abcdex。
搞清了前后缀,我们来求它的next[]数组。
1 2 3 4 5 6 //j代表位置下标
a b c d e x //模式串
-1 0 0 0 0 0 //next[]数组值
next数组推导如下
当j=1时,next[1]=-1;//因为a之前没有字符,默认-1。
当j=2时,next[2]=0;//因为b之前有字符a,没有相等的前缀和后缀,长度为0。
当j=3时,next[3]=0;//因为c之前有字符串ab,前缀和后缀不相等,长度为0。
当j=4时,next[4]=0;//因为d之前有字符串abc,前缀和后缀不相等,长度为0。
当j=5时,next[5]=0;//因为e之前有字符串abcd,前缀和后缀不相等,长度为0。
以此类推…
相信你还有点糊涂,不过没关系,看下来一个例子,你就清楚了。
1 2 3 4 5 6 7 8 9
a b a b a a a b a
-1 0 0 1 2 3 1 1 2
当j=1时,next[1]=-1;
当j=2时,next[2]=0;
当j=3时,next[3]=0;
当j=4时,next[4]=1;//因为在b之前有字符串aba,前缀和后缀相同,并长度为1;
当j=5时,next[5]=2;//因为在a之前有字符串ab ab,前缀和后缀相同,并长度为2;
依次类推…
接下来我们自定义一个求next[]数组的函数吧
void Getnext(char str[])
{
int i=0,j=-1;//初始化
next[0]=0;//next[]为一个全局数组
while(i<strlen(str))
{
if(j==-1||str[i]==str[j])
next[++i]=++j;
else
j=next[j];//回溯
}
}
如果光看这个代码自己还是不理解,可以自己手动运行一遍,有规律可寻。
现在最核心的一步结束了,现在我们就得运用我们所求的next[]数组,来查询位置。
void Kmp(char str[],char mol[])//str为主串,mol为子串
{
int i,j,len1,len2;
i=0;
j=0;
len1=strlen(str);
len2=strlen(mol);
while(i<len1&&j<len2)
{
if(j==0&&str[i]!=mol[j])
i++;
else if(j>0&&str[i]!=mol[j])
j=next[j];
else
{
i++;
j++;
}
}
if(j>=len2)
printf("%d\n",i-j+1);
else
printf("0\n");
}
总结:还是一句话,在kmp算法中最重要的就是求next[]数组并且理解它,然后运用它来解决各种问题。