【字符串】KMP字符串匹配

百度百科

Definition

\(KMP\)算法是一个字符串匹配算法。他接收两个字符串\(A,B\),返回\(B\)\(A\)中出现的所有位置。

以下称需要被匹配的串\(A\)为主串,可能在主串中多次出现的串\(B\)为模式串。约定主串的长度为\(n\),模式串的长度为\(m\)

朴素的想法显然是对使用两个指针\(i,j\)分别指向\(A,B\)。枚举\(i\)的位置,暴力右移\(j\)观察是否匹配。复杂度\(O(nm)\)

考虑当一个位置失配的时候,如果主串的当前位置与模式串前面的某些位置相同,则可以直接跳到前面的位置,而不用从头开始。

举个例子:

主串:aaabbbc

模式串:aabbbc。

从主串第一个位置开始匹配,当\(i=2,j=2\)时,发现\(i+1\)\(j+1\)失配,但是考虑完全不需要从第二个位置令\(i=2,j=1\)重新匹配,而是直接令\(j\)等于可以与\(i\)匹配的上一个前缀,即可重新匹配。所谓上一个前缀,设\(j\)被赋值为\(k\),则\(k\)满足主串[i-k~i]==模式串[1~k]。我们可以使用一个\(next\)数组直接预处理这个前缀,然后直接在失配时令模式串指针向前按照\(next\)数组向前跳即可。

考虑\(next\)数组的求法:把模式串错一位与自身匹配。j跳到的位置显然就是当前位置的\(next\)

Example

传送门

Description

如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置。

为了减少骗分的情况,接下来还要输出子串的前缀数组next。

Input

两行分别是\(s1\)\(s2\)

Output

若干行,每行包含一个整数,表示s2在s1中出现的位置

接下来1行,包括length(s2)个整数,表示前缀数组next[i]的值。

Hint

字符串长度不超过\(10^6\)

Code

#include<cstdio>
#include<cstring>
#define rg register
#define ci const int
#define cl const long long

typedef long long int ll;

template <typename T>
inline void qr(T &x) {
    rg char ch=getchar(),lst=' ';
    while((ch > '9') || (ch < '0')) lst=ch,ch=getchar();
    while((ch >= '0') && (ch <= '9')) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(lst == '-') x=-x;
}

namespace IO {
    char buf[120];
}

template <typename T>
inline void qw(T x,const char aft,const bool pt) {
    if(x < 0) {x=-x,putchar('-');}
    rg int top=0;
    do {IO::buf[++top]=x%10+'0';} while(x/=10);
    while(top) putchar(IO::buf[top--]);
    if(pt) putchar(aft);
}

template <typename T>
inline T mmax(const T a,const T b) {return a > b ? a : b;}
template <typename T>
inline T mmin(const T a,const T b) {return a < b ? a : b;}
template <typename T>
inline T mabs(const T a) {return a < 0 ? -a : a;}

template <typename T>
inline void mswap(T &_a,T &_b) {
    T _temp=_a;_a=_b;_b=_temp;
}

const int maxn = 1000010;

char s1[maxn],s2[maxn];
int nxt[maxn];

void KMP(char *a,char *b,int l1,int l2,bool pt) {
    rg int i,j=0;
    for(i=pt?1:2;i<=l1;++i) {
        while(j&&(b[j+1] != a[i])) j=nxt[j];
        if(b[j+1] == a[i]) ++j;
        if(!pt) nxt[i]=j;
        if(j == l2) {
            qw(i-l2+1,'\n',true);
        }
    }
}

int main() {
    scanf("%s\n%s",s1+1,s2+1);
    int l1=strlen(s1+1),l2=strlen(s2+1);
    KMP(s2,s2,l2,l2,false);
    KMP(s1,s2,l1,l2,true);
    for(rg int i=1;i<l2;++i) qw(nxt[i],' ',true);
    qw(nxt[l2],'\n',true);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/yifusuyi/p/9878556.html