KMP算法(字符串匹配)

遇到字符串匹配问题,一般我就只能想到O(nm)的朴素算法...
今天有这样一种算法,使得复杂度变为O(n),
这就是KMP(烤馍片)算法
粘一个模板题先:
给出两个字符串\(s_1\)\(s_2\),其中\(s_2\)\(s_1\)的子串,求出\(s_2\)\(s_1\)中所有出现的位置。
然后本题还要求输出所有\(s_2\)中字符的前缀数组,
现在留下一个疑点,前缀数组(这是啥?),先往后看
首先确定一点,就是在进行字符串匹配时,会是拿一个字符串与另一个字符串的一部分进行比较,那么我们有以下称呼(拿本题进行举例):\(s_2\)叫做模式串,\(s_1\)叫做母串
我们先分析朴素算法的原理与弊端:
朴素算法是将每一个母串字符拿出比较,一旦不符合就会放弃前面匹配的所有信息,重头再来,
然而KMP不然,KMP就是一种"失败为成功之母"的算法,每次在匹配失败时利用前面信息进行更高效的匹配,
具体的思想需要用到这个"前缀数组"
那么这是啥呢?
先考虑一个问题:如何利用失败信息,
如果匹配失败,我们会放弃当前匹配,然而这起码证明前面的匹配都是成功的,也就是说,只要我找到模式串前面的能与自己(当前匹配失败之前的一个位置)匹配上的子串,那么一定也能与前面匹配,进行再次查找,而不用进行朴素算法暴力枚举,
前缀数组就是保存这样的指针的数组,预处理大概是这样的:

inline void init(){
    p[1]=0;
    int j=0;
    for(int i=1;i0&&b[j+1]!=b[i+1]) j=p[j];
        if(b[j+1]==b[i+1]) j++;
        p[i+1]=j;
    }
}

总体代码是这样的:

#include
#include
#include
using namespace std;
char a[1000005];
char b[1000005];
int p[1000005];
int n,m;
inline void init(){
    p[1]=0;
    int j=0;
    for(int i=1;i0&&b[j+1]!=b[i+1]) j=p[j];
        if(b[j+1]==b[i+1]) j++;
        p[i+1]=j;
    }
}
inline void find(){
    int j=0;
    for(int i=0;i0&&a[i+1]!=b[j+1]) j=p[j];
        if(b[j+1]==a[i+1]) j++;
        if(j==m){
            printf("%d\n",i-m+2);
            j=p[j];
        }
    }
}
int main(){
    scanf("%s%s",a+1,b+1);
    n=strlen(a+1);m=strlen(b+1);
    init();
    find();
    for(int i=1;i<=m;i++)
        printf("%d ",p[i]);
    return 0;
}

为什么KMP主体与预处理这么像呢?
因为预处理本身就是
我    匹配     我自己
最后就是输出前缀数组了

转载于:https://www.cnblogs.com/648-233/p/11117987.html

你可能感兴趣的:(KMP算法(字符串匹配))