数据结构之KMP
程序员文章站
2022-07-14 08:24:32
...
KMP主要用于字符串匹配,比如给一个长度为n的字符串s,然后再给一个长度为m的字符串t,问s中有没有连续子串和t一样(n>=m)
显然,直接暴力求解,容易得到最坏O(m*(n-m))复杂度的算法。当这个朴素算法的复杂度不满足要求的时候,就需要用到我们经典的字符串匹配算法——KMP,复杂度为O(m+n)
如下图所示,i指针指向字符串s,j指针指向t,当i从第一个字符a开始与t匹配,当i指到红框里面的那个c,j指向d,发现c和d不匹配(称为失配)。按照暴力的方法,此次失配后,i将回到s的第二个字符b,而j回到t的起点,依次匹配,但实际上,因为我们已经知道了i前面是abccab,也就是说i往前移只会重复匹配,很多情况下并不会匹配成功——由图中可知,i最多要向前移动两位,才会使得s[i]==t[0],但这时j要指向t的开始——实际上i向前移动两位也是一种重复匹配,还是因为我们已经知道i前面的字符是abccab——显然,i和j之前都是完全匹配的,为了不重复匹配,i是不能前移的,那么只能让j前移了,假设j前移到了k位,正好和i匹配了,也就是说abccab的前k-1位和后k-1位完全吻合,并且abccab的前k位即t[k]要等于s[i],所以j就移到了那个c处
可以说理解了上面那个图,就理解了KMP
额,要是不能理解,我也扯不下去了(我的博客大概是全网说KMP的说的最短的了吧~)这玩意儿好难讲啊,还是直接上代码吧~~~
// 在字符串T里找P,输出所有匹配点
#include <iostream>
#include <cstring>
using namespace std;
const int sz = 1010;
int f[sz];
void getFail(char* p, int* f) // 由p预处理处f[],f[x]表示上文中的j指针跑到x时发现不匹配,应该跳到f[x]处继续匹配
{
int m = strlen(p);
f[0] = 0, f[1] = 0;
for(int i = 1; i < m; ++ i)
{
int j = f[i];
while(j && p[i] != p[j]) j = f[j];
f[i+1] = p[i] == p[j] ? j+1 : 0;
}
}
void find(char* T, char* P, int* f)//KMP匹配过程
{
int n = strlen(T), m = strlen(P);
getFail(P, f);
int j = 0;
for(int i = 0; i < n; ++ i)
{
while(j && P[j] != T[i]) j = f[j];
if(P[j] == T[i]) ++ j;
if(j == m)
printf("%d\n", i-m+1);
}
}
int main()
{
char P[sz] = "", T[sz] = "";
scanf("%s%s", T, P);
find(T, P, f);
return 0;
}
上一篇: Eclipse 3.5.1 Galileo SR1发布
下一篇: KMP算法(深入理解)