kmp算法最主要的思路就是根据已有的信息让比较字符串的工作在一个循环内完成,最核心的问题在于求出保存信息的数组next
首先声明,txt是主串,pat是模式串,next是信息数组。
while循环部分就是求next数组的部分。
#include<stdio.h>
#define MAX 50
int main()
{
char txt[MAX];
char pat[MAX];
int i, j, k;
int next[MAX] = { 0 }; //初始化数组使得所有元素为0;
fgets(pat, MAX, stdin);
fgets(txt, MAX, stdin);
k = 0;
j = 1;
i = 0;
while(pat[j]!='\n')
{
j++;
if (k == 0 || pat[k] == pat[j])
{
if (pat[k] == pat[j])
next[j] = k++;
else
next[j] = k;
}
else
k = next[k];
}
首先要做的就是理解next数组,简单来说,这个数组的含义为,当字符串中pat[n]与txt[m]比较不符的时候,也就是pat[n]!=txt[m]时,我们把n转移到next[n]去,就像是游标卡尺下面的副尺一样,上面的主尺贴着txt,下面的副尺贴着pat,你从txt[0]开始,向右移动副尺,和txt一个一个比较,如果有重叠,就把结果输出,如果一直到主尺底,也就是txt数组的最后也没有重叠,那就没有结果,问题在于,如何用算法实现此过程。
假如pat={A,C,B,D,A,C,C},txt={S,J,J,A,C,B,D,A,C,B,D,A,C,C}
int k = 0;
for (int i = 0; i < txt.len; i++)
{
if (pat[k] == txt[i])
k++;
....
} //伪码,只是示意txt遍历而已,k还没有处理
这个代码遍历txt来和pat比较,我们发现在txt中有一段A,C,B,D,A,C,B这么一段,它和pat只在最后一个字母不符合,pat最后一个是C,当比较到最后一个的时候,虽然最后一个字母不符合pat,但是如果比较就会发现,B,是pat的第三个字母,可以把整个pat移动一下,把pat的头移动对应到A,C,B,这样后面的字母也就都对上了。相当于,next[6]=3,意义是,当pat[6]比较失败时,将pat[3]与这个字符再次进行比较。
这就是next的作用,他是一个信息数组,和pat一一对应,next[n]表明,当pat[k]!=txt[i]时(这里隐含一个条件,要知道,我们在比较的时候,如果前面的字符比较失败,根本就不会比较到n,也就是说,pat[n]出错时,意味这前n个字符都比较成功了),一一对应的next[n]上的值就是下一个k的值,也就是k=next[k],也就是说,执行一遍pat[next[k]]==txt[i]的判断,因为有可能这个比较失败的字符txt[i]和pat开头的某些字符是吻合的。
那么现在的问题就是如何求出next数组。
while(pat[j]!='\n')
{
j++;
if (k == 0 || pat[k] == pat[j])
{
if (pat[k] == pat[j])
next[j] = k++;
else
next[j] = k;
}
else
k = next[k];
}
如果说pat串里没有任何相同的字母,比如说pat[]="A,B,C,D,E,F,G",那么next数组将全部为0,意义是,不论pat串哪里出错,都将指向pat[0]进行比较,所以本质上,next数组的意思就是 next[i] = len,长度为i的数组的前缀和后缀相等的最大长度。 例如abcdabc就是 next[7] = 3; 相等的前缀和后缀最长是abc长度为3。
理解的next数组就非常好办了,利用pat串生成next,k=next[k]可能有点难理解,这个就是通过next自身的特性来赋值k,这个有图会比较好看,现在条件难以画图,所以就推荐几个其他我学习时有所帮助的博客。
(后面有时间了,也许会作图再写,但是。。。)