数据结构之KMP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Q1410136042/article/details/82156948

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;
}

猜你喜欢

转载自blog.csdn.net/Q1410136042/article/details/82156948
今日推荐