KMP算法是实现单模式匹配的一种算法,而AC算法是实现多模式匹配的算法,两者的区别主要就是多模式和单模式的问题。
一.KMP算法
KMP算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,其对于任何模式和目标序列,都可以在线性时间内完成匹配查找,而不会发生退化,是一个非常优秀的模式匹配算法。其实KMP算法与BF算法(简单的模式匹配,每次失败都从头再来)的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n)。在KMP算法中,为了确定在匹配不成功时,下次匹配时j的位置,引入了next[]数组,next[j]的值表示P[0...j-1]中最长后缀的长度等于相同字符序列的前缀。
对于next[]数组的定义如下:编号从1开始
1) next[j] = 0:j = 1时
2) next[j] = max(k): 1<k<j P[1...k]=P[j-k+1,j-1]
3) next[j] = 0 其他
如下:
以模式字符串abcabcacab为例
j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
pattern[j] | a | b | c | a | b | c | a | c | a | b |
next[j] | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 5 | 0 | 1 |
手工求解Next数组的方法如下:
(1)next[1] = 0;next[2] = 1;(next[0]不使用)
(2)后面求解每一位next[j]的值时,根据j的前一位的next[j-1]求解,令k = next[j-1];
(3)比较S[j-1]与S[k]
a.若相等,next[j] = k+1;
b.若不等,令k=next[k],若K != 0,回到(3),直到next[k] = 0;next[j] = 1.
代码实现:
void get_next(char* T,int next[])
{
k = -1;
next[1] = -1;
j = 0;
while(j < (strlen(T)-1))//T[0]存储字符串的长度
{
if(k == -1 || T[k] == T[j])
{
++k;++j;next[j] = k;
}
else
k = next[k];
}
}
因此KMP算法的思想就是:在匹配过程称,若发生不匹配的情况,如果next[j]>=0,则主串(S)的指针i不变,将模式串(T)的指针j移动到next[j]的位置继续进行匹配;若next[j]=0,则将i,j均右移一位(其实此时模式串在第一位),继续进行比较。
代码实现如下:
int KMP(char* s,char* p)
{
int i = 0;
int j = 0,int next[100];
while(i < strlen(s))
{
if(j == -1 || S[i] == T[j])
{
i++;
j++;
}
else
j = next[j];
}
}
测试代码
// KMP.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
using namespace std;
void GetNext(char* p,int* next)
{
int j = 0;
int k = -1;
next[0] = -1;
while (j < (strlen(p)-1))
{
if (k == -1 || p[j] == p[k])
{
k++;j++;
next[j] = k;
}
else
k = next[k];
}
}
int KMP(char* s,char* p)
{
int len1 = strlen(s);
int len2 = strlen(p);
int i = 0;
int j = 0;
int next[100];
GetNext(p, next);
while (i < len1)
{
if (s[i] == p[j] || j == -1)
{
i++;
j++;
}
else
j = next[j];
if (j == len2)
return i - len2;
}
return -1;
}
int main()
{
char s[100] = "abcabaaabaabcac";
char p[100] = "abaabcac";
int result = KMP(s, p);
cout << result << endl;
return 0;
}