最近花了些时间学习kmp算法(看毛片算法),对于没有基础的人来说,开始学一个东西都会非常痛苦,我也是这样。反反复覆读了好久相关的文章,终于有些明白是怎么回事了,这里给大家推荐下我的学习路径。
首先,我推荐大家看阮一峯的博客文章——字符串匹配的kmp算法。
这篇文章篇幅不长,但写得非常清楚明白,文章中用例子给出了kmp到底是怎么操作的一个过程,不过并没有给出代码。我自己看懂了以后,就直接写了一个kmp,当然这是初级版的kmp,写得比较囉嗦,而且优化的不是很好,不过毕竟能写了呀。
其次,我再推荐大家看matrix67的文章——kmp算法详解
阮一峯的博客主要讲具体实现过程,而matrix67则简单介绍了kmp的原理是怎么回事,而且其中还给出了伪代码,也就是优化以后的kmp写法。我自己在看的时候,其实对匹配数组用前面推出后面的内容理解得不是很明白,所以我又从另外一篇 从头到尾彻底理解kmp 对照着看,这篇文章讲了实现,也讲了原理,后面还有实际代码给出,只不过篇幅比较长,容易看着看着就没耐心。
这三篇文章,他们对于匹配数组(next数组)具体的定义并不全然相同,但本质是一回事。
阮一峯定义匹配数组是前后缀最大公共子列,然后移动位数用已匹配的字符数-匹配数组的值。而matrix67则是如果后一位不匹配,就移动到哪一位,后两篇博客的next数组其实就是前后移了一位,本质相同,阮一峯的匹配就是把他们移位的情况在具体分解出来,怎么移位的告诉大家,所以本质上,他们都相同。
看完这些以后,我自己对kmp就有初步的理解,自己也能裸敲出matrix67的那套代码,接下来就是敲敲题来巩固了。
下面是我的代码:
看完阮一峯博客后写得:
KMP第一个版本:
#include <iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
int givenum(string a);//给出具体某个数的匹配值
int kmp(string a, string standard);// 字符匹配
int getpart(string a); //得到part数组
int part[20];//部分匹配数组
int main()
{
memset(part, 0, sizeof(part));
string duru;
cin >> duru;
cin.get();
getpart(duru);
string tocompare;
getline(cin, tocompare);
cout << (kmp(tocompare, duru)) << endl;
return 0;
}
int getpart(string a)
{
int len = a.length();
string temp;
for (int i = 0; i < len; i++)
{
temp += a[i];
part[i] = givenum(temp);
}
return 0;
}
int kmp(string a, string standard)
{
int alen = a.length();
int slen = standard.length();
int flag = 0;
int aloca = 0, sloca = 0;
while (aloca != alen)
{
if (a[aloca] == standard[sloca])
{
if (sloca == slen - 1)
{
flag++;
aloca++;
sloca = 0;
}
else
{
aloca++;
sloca++;
}
}
else
{
if (sloca == 0)
{
aloca++;
}
else
{
sloca = part[sloca - 1];
}
}
}
return flag;
}
int givenum(string a)
{
int len = a.length();
int maxit = 0;
for (int i = 0; i < len; i++)
{
int cnt = 0;
for (int j = 0; j < i; j++)
{
if (a[j] == a[j + len - i])
cnt++;
else
{
cnt = 0;
break;
}
}
if (cnt > maxit)
maxit = cnt;
}
return maxit;
}
后来写得(做了优化):
#include<iostream>
#include<string>
using namespace std;
int getnext(string tocmp, int next[]);
int kmp(string storeit, string tocmp, int next[]);
int flag = 0;
int main()
{
string storeit;
getline(cin, storeit);
string tocmp;
getline(cin, tocmp);
int next[100];
getnext(tocmp, next);
kmp(storeit, tocmp, next);
cout << flag << endl;
return 0;
}
int kmp(string storeit, string tocmp, int next[])
{
int slen = storeit.length();
int tlen = tocmp.length();
int j = -1;
for (int i = 0; i < slen; i++)
{
while (j >= 0 && tocmp[j + 1] != storeit[i])
j = next[j];
if (tocmp[j + 1] == storeit[i])
j++;
if (j == tlen - 1)
{
flag++;
j = -1;
}
}
return 0;
}
int getnext(string tocmp, int next[])
{
int len = tocmp.length();
next[0] = -1;
int j = -1;
for (int i = 1; i < len; i++)
{
while (j >= 0 && tocmp[i] != tocmp[j + 1])
j = next[j];
if (tocmp[i] == tocmp[j + 1])
j++;
next[i] = j;
}
return 0;
}