保證一看就明白的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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章