欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据结构之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

额,要是不能理解,我也扯不下去了(我的博客大概是全网说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;
}

 

相关标签: KMP