最近看了kmp算法,经过反复的学习终于感觉完全弄懂了kmp算法,现做一个小结。
kmp算法在进行字符串匹配的时候,如果当前位置j匹配不成功,那么模式串向前移动的位数由next[j]的值决定。next数组的位置j的值表示的是模式串p中p[0~j-i]子串的前缀和后缀相匹配的最长长度,即如果next[j]=k,表示p[0~k-1]==p[j-k~j-i],而且p[k]!=p[j]。
next数组的值有三类:
- -1:表示j为0或者不存在前后缀相等的子串,而且p[j]==p[0]。这个情况在匹配失效的时候,下一步应该将目标串当前位置加一和模式串的第一个位置比较
- 0:表示不存在前后缀相等的情况,但是p[0]!=p[j]。这个情况在匹配失效时,下一步将目标串当前比较位置与模式串的第一个位置比较(这个时候和上一个不同在于,p[0] != p[j]要求从目标串的当前位置比较)
- k(k>0):表示存在最大长度为k的前缀后缀字串相匹配。这个情况下,下一步将目标串当前位置与模式串的第k+1个位置比较,即与p[k]比较。
next值的求解代码为:
void getNext(const string str,vector<int>& next)
{
next[0] = -1;
int j = -1;
for(size_t i = 0;i < str.size()-1;)
{
if (j == -1 || str[i] == str[j])//j表示当要计算next[i+1]时str[0...i-1]前后缀相等的子串的最长长度(-1代表没有相等的前后缀,计算next[i+1]使用了前面的信息
{
++i;
++j;
if (str[i] != str[j])
next[i] = j;
else
next[i] = next[j];
}
else
j = next[j];
}
}
kmp算法代码如下所示:
int kmp(const string src,const string pattern)
{
vector<int> next(pattern.size());
getNext(pattern,next);
for (int i = 0;i < next.size();i++)
cout << next[i] << " ";
cout << endl;
int i = 0;
int j = 0;
while (i < src.size() && j < pattern.size())
{
if (src[i] == pattern[j])
{
++i;//注意,这里最后一次匹配成功了仍然会加一,所以在最后返回的时候不用加一
++j;
}
else if (next[j] == -1)
{
++i;
j = 0;
}
else
j = next[j];
}
if (j >= pattern.size())
return i - pattern.size();
else
return -1;
}
以上就是KMP算法的完整实现。要理解kmp算法,关键是理解next数组值的含义,还有next数组的求解过程。