今天刚听学长讲了这个算法,听完感觉懂了又不是很懂,所以自己就赶紧跑来总结一下子。
首先要理解,朴素的单模式串匹配大概就是枚举每一个文本串元素,然后从这一位开始不断向后比较,每次匹配失败之后都要从头开始重新匹配,但是之前可能存在已经匹配好了的字符却还要再匹配一遍,感觉超级麻烦。
所以就有了kmp这个算法
对于每次匹配之后,我都不会从头重新开始枚举,而是根据我已经得知的数据,从某个特定的位置开始匹配;而对于模式串的每一位,都有唯一的“特定变化位置”,这个在匹配之后的特定变化位置可以帮助我们利用已有的数据不用从头匹配,从而节约时间。
这里有两个字符串a和b,a是文本串,b是模式串,b串的字符数量要远远小于a串
我们定义两个指针i、j,i是a串的指针,j是b串的指针
当匹配到j == blen时,结束
那么,如果按照平常的办法处理,一位一位的匹配,不匹配就再从i+1开始,明明b串里有匹配的字符却还要再重新匹配一遍
就好比:
ababababe
ababe
匹配到e的时候你会发现不匹配了,但是这时候前四位是匹配的,难道还要再匹配一遍的吗…
我们完全可以这样:
ababababe
…ababe
所以这时候就要预处理相同的部分
定义一个kmp数组,用模式串自己匹配自己来处理kmp数组
我们用j来表示模式串匹配到了第几位
如果匹配,相对应的j++
下面来看看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
int kmp[maxn];
char a[maxn],b[maxn];
int alen,blen,j;
int main()
{
cin >> a+1; cin >> b+1;
alen = strlen(a+1);
blen = strlen(b+1);
for(int i=2;i<=blen;i++)
{
while(j && b[i] != b[j+1])//判断j是否为0是因为,如果回跳到第一个字符就不用了再回跳了
j = kmp[j];//通过自己和自己匹配来得出每一个点的kmp值
if(b[i] == b[j+1]) j++;
kmp[i] = j;
}//b串自己处理自己
j=0;
for(int i=1;i<=alen;i++)
{
while(j>0 && a[i] != b[j+1])//如果失配,那么就不断向回跳,直到可以继续匹配
j = kmp[j];//记录此刻kmp[j]的位置,下一次从这里开始
if(a[i] == b[j+1]) j++;//如果匹配成功,对应模式串位置j++
if(j == blen)
{
cout<< i-blen+1 << endl;//此刻在a串出现的位置就是i-blen+1
j = kmp[j];
}
}
for(int i=1;i<=blen;i++)
cout << kmp[i] << " ";//输出kmp数组
return 0;
}