总结:KMP算法的重点是求next数组。注意,该数组只和模式串有关系,和要匹配的串没有关系。
两种写法的基本思想是一致的。只是求next数组的方式不同,next数组所得的值也有所不同,代表的含义也不同。
原理不多说,网上讲的很多,这里只贴出完整的实现代码。
一般网上讲解的求next数组的都是针对方法二的。
第一种优化写法。
next[i]表示匹配串在i处如果匹配失败下次移到的位置.
const int N = 100;
int next[N];
int pattern_len, str_len;
char pattern[N];
char str[N];
//求next数组的值. 即预处理
void getNext1()
{
int i = 0, j = -1;
next[0] = -1;
while (i < pattern_len - 1)
{
//cout << i << " " << j << endl;
if (j == -1 || pattern[i] == pattern[j])
{
++i;
++j;
if (pattern[i] != pattern[j])
next[i] = j;
else
next[i] = next[j];
}
else
j = next[j];
}
}
int kmp1()
{
int i = 0, j = 0;
str_len = strlen(str);
while (i < str_len && j < pattern_len)
{
if (j == -1 || str[i] == pattern[j])
{
++i;
++j;
}
else
j = next[j];
}
if (j >= pattern_len)
return i - pattern_len;
else
return -1;
}
第二种,经典写法.
next[i]表示 最远匹配到的位置。
以ABCDABD为例:-1 -1 -1 -1 0 1 -1
abcbabc : -1 -1 -1 -1 0 1 2
void getNext2()
{
next[0] = -1;
int pre; //注,此处的i是从1开始的
for (int i = 1; i < pattern_len; ++i)
{
pre = next[i - 1];
//向前回溯 pre
while (pre >= 0 && pattern[pre + 1] != pattern[i])
{
pre = next[pre];
// cout << i << " index: " << pre << " back " << endl ;
}
// cout << "i:" <<i << " " << pre << endl;
if (pattern[pre + 1] == pattern[i])
next[i] = pre + 1;
else
next[i] = -1; //由于前面已经回溯,这里可直接赋值为-1
}
}
int kmp2()
{
int p_index = 0, t_index = 0;
while (p_index < pattern_len && t_index < str_len)
{
if (str[t_index] == pattern[p_index])
{
++t_index;
++p_index;
}
else if (p_index == 0)
++t_index;
else
p_index = next[p_index - 1] + 1;
}
if (p_index == pattern_len)
return t_index - pattern_len;
else
return -1;
}
int main()
{
freopen("in.txt", "r", stdin);
while(gets(str))
{
gets(pattern);
pattern_len = strlen(pattern);
//if(str[0] == '#') continue;
getNext1();
int ans = kmp1();
cout << "next1: ";
for(int i=0; i<pattern_len; i++) cout << next[i] << " ";
cout << endl;
cout << ans << endl;
getNext2();
cout << "next2: ";
for(int i=0; i<pattern_len; i++) cout << next[i] << " ";
cout << endl;
ans = kmp2();
cout << ans << endl;
}
}
测试数据:
/*
abadfabaabacd
abac
ababcbabcbaca
abcbabc
*/
next1: -1 0 -1 1
8
next2: -1 -1 0 -1
8
next1: -1 0 0 0 -1 0 0
2
next2: -1 -1 -1 -1 0 1 2
2