先附上一份我觉得解释很详细的文章:
匹配基本流程和前后缀
next数组求法与解释
KMP算法之所以是线性复杂度,就在于不需要重复匹配对比,通过next调到最近的有必要匹配检查的位置。
换句话说,就是对于被匹配串,每当在当前节点发生不匹配,为了不浪费之前匹配成果和避免遗漏,跳到拥有相同前缀的位置继续比较。
不懂?不要紧,看着下面代码注释,相信会有新的发现。
奉上一道水题:
剪花布条
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
Input
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
Output
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
Sample Input
abcde a3
aaaaaa aa
#
Sample Output
0
3
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxsize 1010
//str1是源字符串,str2是需要寻找的字符串
char str1[maxsize],str2[maxsize];
//记录跳表
int Next[maxsize];
//获得跳表
void getnext()
{
int i=0,j=-1;//i是当前找到的位置,j是从开头找
//
Next[0]=-1;
int m=strlen(str2);
//为每一个位置上的字母寻找跳表值
while(i<m)
{
//如果是-1,代表刚开始或者前一位置匹配失败,且不存在拥有相同前缀的组成
//后者比较好理解,就是前缀和后缀当前位置拥有相同字符,
//每个位置next的值是当前位置前缀和后缀拥有的最大公共长度
//当新读入的一位和前缀读到的位置的字母相同,自然最大长度会加一
if(j==-1||str2[i]==str2[j])
{
i++;
j++;
Next[i]=j;
}
//新读入的一位和前缀读到的位置的字母不相同,自然最大长度从头开始重新找
//因为前缀一定从第一位开始向后计算
//所以一旦不一样,就可以跳到第next【j】个位置
//(如果j不为0,说明有一段是匹配的,也就是从开头到第j个之前还是一样的)
//此时,如果是-1,说明之前从没有到达这个长度,此时只能从头开始重新计算,接上一步j==-1
//如果不是-1而是确切的值,说明之前有过有相同前缀的子串,则在此基础上继续找,接上一步str2[i]==str2[j]
else
{
j=Next[j];
}
}
}
//匹配过程
int kmp()
{
int n=strlen(str1);
int m=strlen(str2);
int i=0,j=0,cnt=0;
while(i<n)
{
//如果重头来或者相同
if(j==-1||str1[i]==str2[j])
{
i++;
j++;
}
else
{
j=Next[j];
}
//完全匹配
if(j==m)
{
j=0;
str1[i-1]='0';
cnt++;
}
}
return cnt;
}
int main()
{
while(scanf("%s",str1))
{
memset(Next,-1,sizeof(Next));
if(str1[0]=='#') break;
else
{
scanf("%s",str2);
getnext();
printf("%d\n",kmp());
}
}
return 0;
}
本人萌新小白一枚,若有错误,还望不吝赐教。