最近花了些時間學習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;
}