有点长,请耐心看完
在上一篇博文中了解了BF算法的代码,BF(Brute-Force)算法从它的英文简称都可以知道是靠蛮力的的匹配关键字的方法,其实就是将模式串与主串的第一个(按照书上的说法,也可以不一定是第一个)依次一个一个的往后面比较,注意,我说的是主串的每一个,也确实够蛮力的。
我们来回顾一下:
- 先声明一下:我们称主串为s,i是它的指针;模式串为t,j是指针,其实就是他们的序列位数,因为数组的位数是从零开始的,所以我们的
字符串的第1个我们称它为第0位
。
if (s[i] == t[j])
{
i++;
j++;
}
else
{
i = i-j+2;//其实就是继续比较主串的后一个字符,就是说可以另设一个参数从0开始,然后失配一次就加 1。(此处是为了和课本上统一)
j = 1;
}
KMP算法同样也是查找关键字的算法,算是BF算法的改进版吧,因为BF中的主串指针回溯问题被巧妙的解决了,其实大家也都知道就是利用课本上前后缀这个概念创造了一个 next 数组(书本上是函数,我更喜欢叫他它数组)。
再来对比看一下KMP与BF的区别:
if (s[i] == t[j])
{
i++;
j++;
}
else
{
j = next[j];//与BF的区别
}
我们马上明白了,我们可以用next[j] 来表示 j 的下一次位置,那么 next[] 这个数组怎么求呢?
int len_t = strlen(t) - 1;
next[0] = -1;
int j = 0;
int k = -1;
while (j < len_t)
{
if (k == -1 || t[j] == t[k])
{
if (t[++j] == t[++k]) //就这一个判断就是书上说的称为next函数的优化
next[j] = next[k];//这一步很难理解
}
else
k = next[k]; //这一步很难,但是认真去演示一下也能理解
}
自己在草稿纸上任意写个串演示一下就会很清楚了;
注意:我刚刚说的,我们的 j 是从0开始的;
if(t[++j] == t[++k]) next[j] = next[k]
这一步,这是对next函数的优化,注意next[j] = k 这个概念,是说k就是 j的下一次位置,当我们发现像 ABAB 这种串,当j移动到第3位(字母“B”)时,若此时才失配,而next[j]=1
,也就是第2个B的位置,可是刚刚我们就是因为B而失配的,所以j就算移动下一位置还是失配,那我们就直接移动到下一位置的的下一位置即当t[++j] == t[++k])
时,next[j] = next[k]
;k = next[k]
这一步,从程序中看出能执行这一步那是因为if (k == -1 || t[j] == t[k])
不满足,可是,在j前面的肯定有满足的,有点递归的思想,然后前后又分成两个串来看,就找到了。
其实就是当前已经求到next[j],接着就应该求解next[j+1],此时就分两种情况,一种是:重复的字符串个数会增加,即所谓的p[k]=p[j],此时p[j+1]=k+1;即p[++j]=++k;另一种就是不能增加,也就是说P[k]!=P[j],即最大重复子串的长度不能增加了
现在再来看完整的代码
我希望看的时候能够从主函数开始从下往上看:
#include<stdio.h>
#include<string.h> //提供strlen()函数计算字符串的长度
// KMP最核心之处,就是求课本上求next[]数组如下:
int getnext(char *t, int next[]) //自己定义一个 getnext()函数
{
int len_t = strlen(t) - 1;
next[0] = -1;
int j = 0;
int k = -1;
while (j < len_t)
{
if (k == -1 || t[j] == t[k])
{
if(t[++j] == t[++k])
next[j] = k;
}
else
{
k = next[k]; //这一步很难,但是认真去演示一下也能理解
}
}
// 打印出每一个next[j]的值,并不一定需要。(我只是为了美观,,满足我的强迫症)
int i;
for (i = 0; i <= len_t; i++)
{
printf("%d ", next[i]);
}
printf("\n");
}
//KM与BF的不同之处:
int kmp(char *s, char *t, int next[])
{
int i = 0, j = 0;
int len_s = strlen(s) - 1;
int len_t = strlen(t) - 1; //strlen()函数计算长度时不会算上,但是匹配的时候数组末尾含"\0",所以要减 1。(想了好久555)
while (i <= len_s && j <= len_t)
{
if (s[i] == t[j])
{
i++;
j++;
}
else
{
if (next[j] != -1) //最初我一直以为需要去定义一个next函数
{
j = next[j]; //KMP优势,相比BF,i不用回溯了
}
else
{
i++;
j = 0;
}
}
}
if (j > len_t)
return i - len_t;
else
return -1;
}
int main()
{
int next[50];
char *str1 = "lx love zlr";
char *str2 = "love";
int len_s = strlen(str1) - 1; //strlen()函数计算长度时不会算上,但是匹配的时候数组末尾含"\0",所以要减 1。
int len_t = strlen(str2) - 1; //下面不再赘述
printf("next[j]的值:");
getnext(str2, next); //调用getnext()函数,然后求出next[]数组(想了很久...)
printf("\n关键字出现的位置是:");
printf("%d", kmp(str1, str2, next));//再去看kmp函数
return 0;
}