您的位置:首页 > 其它

KMP算法的实现

2016-08-11 10:51 295 查看
概念:

Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP 算法”,常用于在一个文本串 S 内查找一个模式串 P 的出现位置,这个算法由 Donald Knuth、Vaughan Pratt、James H. Morris 三人于 1977 年联合发表,故取这三人的姓氏命名此算法。

下面先直接给出 KMP 的算法流程:
假设现在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置:
如果 j = -1,或者当前字符匹配成功(即 S[i] == P[j]),都令 i++,j++,继续匹配下一个字符;
如果 j != -1,且当前字符匹配失败(即 S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串 P 相对于文本串 S 向右移动了 j - next [j] 位。

现在我们关系的是next数组,那next数组是如何求得呢?
     

举例:给定字符串“ABCDABD”,可求得它的 next 数组如下:



实现原理:计算 next 数组的方法可以采用递推:
1.如果对于值 k,已有 p0i,p1, ..., pk-1 = pi-k,pi-k+1, ..., pi-1,相当于 next[i] = k。 可以理解为next[i] = k 代表 p[i] 之前的模式串子串中,有长度为 k 的相同前缀和后缀。

2.下面的问题是:已知 next [0, ..., i],如何求出 next [i + 1] 呢?
若p[k] == p[i],则 next[i+ 1 ] = next [i] + 1 = k + 1;
若p[k ] ≠ p[i],如果此时 p[ next[k] ] == p[i ],则 next[ i + 1 ] = next[k] + 1,否则继续递归前缀索引 k = next[k],而后重复此过程。

#include<iostream>
using namespace std;

//求next值
void CalNext(const char *sub, int n, int *next)
{
int i=0,k=-1;
next[0] = -1;

while(i<n-1)//注意是n-1,否则会越界
{
if(k==-1||sub[i] == sub[k])
{
next[++i] = ++k;
}
else
{
k = next[k];
}
}
for(int i=0; i<n; i++) //打印测试
{
cout<<next[i]<<" ";
}
cout<<endl;
}
int KMP(const char *str, const char *sub)
{
if(str == NULL || sub == NULL) return -1;

int i=0,j=0;
int len1 = strlen(str);
int len2 = strlen(sub);
////////////////////////////
int *next = new int[len2];
CalNext(sub, len2, next);
///////////////////////////
while(i<len1&&j<len2)
{
if(j==-1 || str[i] == sub[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
//////////////////////////
delete [] next;
if(j == len2) return i-j;
return -1;
}
int main()
{
char *str = "aaaaaaaaaaaaabbb";
char *sub = "aaaaabb";
int index = KMP(str, sub);
cout<<index<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: