保证一看就明白的KMP算法

最近花了些时间学习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;
}

点赞