KMP算法详解
我们知道BF算法是蛮力法,费时费力,那怎么办呢?D.E.Knuth、J,H,Morris 和 V.R.Pratt 三位大神就为我们创造了一种快速匹配的算法——KMP算法,这种算法的核心就在于它减少了主串指针回溯的过程,大大提高了我们匹配速度。
上面提到主串指针回溯,这是什么东西呢?首先给大家讲一下KMP的实现步骤。
1、步骤
- 获取主串next值
就是因为KMP有了在匹配之前计算了主串的next值,所以就减少了BF的指针回溯。给大家打个比方:
主串:ababbbab
子串:abb
第一轮匹配:匹配到了第一个字符
ababbbab
a
第二轮匹配:匹配到了第二个字符
ababbbab
ab
第三轮匹配:匹配失败
匹配失败之后BF算法是这样的,从主串第二个字符又开始匹配,这就较做主串指针回溯。
ababbbab
abb
下面代码就是求主串的next[]的
void getnext() {
next[0] = -1;
int i = 0, j = -1;
int len = strlen(T);
while(i < len) {
if(j == -1 || T[i] == T[j])
next[++i] = ++j;
else
j = next[j];
}
}
获取next其目的就是找出当匹配失败的那个字符前面的子字符串有没有相同的前缀和后缀,如果有就直接跳过来,这里有点绕,一会截图给大家介绍一下。所以KMP算法就是避免了指针回溯的问题。
更简单一点,回溯算法,又称为“试探法”。解决问题时,每进行一步,都是抱着试试看的态度,如果发现当前选择并不是最好的,或者这么走下去肯定达不到目标,立刻做回退操作重新选择。这种走不通就回退再走的方法就是回溯算法。
进行匹配
bool KMP() {
getnext();
int len1 = strlen(T);
int len2 = strlen(S);
int i = 0, j = 0; //i指向模式串T,j指向主串S
while(j < len2) {
if(T[i] == S[j]) {
i++;
j++;
if(i == len2) {
return true;
}
} else {
i = next[i];
if(i == -1) {
j++;i++;
}
}
}
return false;
}
废话不多说,直接上图。给大家看看上面代码的过程
具体过程:
大家可以看见上面三张图片,最开始的5个是匹配的,当第六个就不匹配了,怎么办呢?就用到了i = next[i]
这个语法,让子串指向第四张图的B,然后从B开始在进行配对。
- 代码
#include <iostream>
#include<cstring>
#include <string>
typedef long long ll;
using namespace std;
bool KMP(char *T,char*S) {
int next[100];
next[0] = -1;
int i = 0, j = -1;
int len = strlen(T);
while(i < len) {
if(j == -1 || T[i] == T[j])
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
int len1 = strlen(T);
int len2 = strlen(S);
int i_ = 0, j_ = 0; //i指向模式串T,j指向主串S
while(j_ < len2) {
if(T[i_] == S[j_]) {
i_++;
j_++;
if(i_ == len2) {
return true;
}
} else {
i_ = next[i_];
if(i_ == -1) {
j_++;i_++;
}
}
}
return false;
}
int main()
{
char a[] = "ABBABBABAB";
char b[] = "ABBAB";
bool m = KMP(a,b);
printf("%d\n",m);
return 0;
}