KMP算法的目的是实现在文本中对某一字符串的快速查找。
先来看一下朴素做法:
string s, p;
for(int i = 1; i <= s.size() - p.size(); i ++ ){
int flag = 1;
if(s[i] == p[1])
for(int j = 1; j <= p.size(); j ++ )
if(s[i + j] != p[j + 1]){
flag = 0;
break;
}
if(flag == 1) cout << i << endl;
}
下面就是思考该如何对朴素做法进行优化。
找到开始比较的坐标是必要的,而逐个字母进行比较似乎也必不可少,那么唯一可以利用的就是每次比较失败后得到的信息。
下面通过例子来说明:(默认字符串从1开始)
字符串s:abababc
字符串p:ababc
如下图,从s[1]开始匹配,匹配到s[5]失败,接下来关键的一步是从s[3]开始匹配。
通过这一步操作,我们就跳过了s[1],实现了优化。
这一步的原理便是整个KMP算法的灵魂。而这个原理则可以描述为:
对于不同的模式串p[ ],在不同匹配失败的位置最大可以跳过的距离该如何计算?
由上述问题,我们应当计算出对于p[i],最大可以跳过的距离next[i]
next[i] = j直观解释为 p[1] ~ p[ j ] 这一段与 p[ i - j + 1] ~ p[ i ] 这一段相等
实际上就是p[1~i]的最长相等前后缀的长度为j
之后的问题就是对next[ ]数组的求解,我们考虑next[ ]数组的递推关系
s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for(int i = 2, j; i <= n; i ++ ){
if(p[ne[i - 1] + 1] == p[i]) ne[i] = ne[i - 1] + 1;
else{
j = ne[i - 1];
while(j && p[j + 1] != p[i]) j = ne[j];
if(!j) ne[i] = 0;
else ne[i] = j + 1;
}
}
求出next[ ]数组后便是最后匹配的过程,代码如下:
for (int i = 1, j = 0; i <= n; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == m)
{
匹配成功后的逻辑
}
}