单模式串匹配----浅谈kmp算法

flag:字符串小结。

模式串匹配,顾名思义,就是看一个串是否在另一个串中出现,出现了几次,在哪个位置出现; 

p.s.  模式串是前者,并且,我们称后一个 (也就是被匹配的串)为文本串;

  在这篇博客的代码里,s1均为文本串,s2均为模式串;

  一般地,文本串长度不小于匹配串;(否则无意义)

很显然可以得到一个暴力的做法 :

for i : 1~lenth_of_s1 {//枚举匹配串在文本串中的开始位置
    for j : 1~lenth_of_s2
        if(s2[j]!=s1[i+j-1]) break;
    if j>lenth_of_s2  //在循环结束前没有break
        output : i
}

 

时间复杂度:O ( TLE )  ------     O (N+M) ~ O(N*M)

所以需要一个更优的算法;

可以发现,在枚举匹配串在文本串中的开始位置时,有很多步骤是无效的,因为匹配串的第一个字符 很有可能和当前枚举到的开始位置 不同;

 

所以可以优化这个过程,每次改变开始位置时,直接移动到下一个和匹配串第一个字符相同的位置 (类似于链表;

int next[N], pos=-1;
char head = s2[1];

for i : lenth_of_s1~1
    if s1[i]==head {
        next[i] = pos;
        pos = i;
    }
next[0] = pos;

for i = next[0] ; i != -1 ; i = next[i] {
    for j : 1~lenth_of_s2
        if(s2[j]!=s1[i+j-1]) break;
    if j>lenth_of_s2  //在循环结束前没有break
        output : i
}

这个做法看起来很强,实际上很容易被卡成O (n^2);

比如说 :s1 :sssssssssssssa, s2 : sssb;

由于并没有利用所有已经匹配过的部分,所以仍然会T;

于是,就有了KMP算法

p.s.  i表示当前在文本串中枚举到的位置,j表示模式串中的;

在s1[ i ] != s2 [ j ]时,将 j 移动到一个在 j 之前的位置k 使得 s2[ 1 ]~s2[ k ] 与 s2[ j-k+1 ]~s2[ j ]完全相同,那么时间复杂度就是O (N+M) 的了;

p.s.  因为 i , j 两个指针最多移动N+M次;

给一个写模板的链接 :https://www.luogu.org/problemnew/show/P3375

贴代码 :

// luogu-judger-enable-o2
// 15owzLy1
//luogu3375_kmp.cpp
//2018 10 02      17:27:50
#include <cstdio>
#include <cstring>
typedef long long ll;
typedef double db;
using namespace std;

const int N = 1000005;
int next[N], la, lb;
char a[N], b[N];

template<typename T>inline void read(T &x_) {
    x_=0;bool f_=0;char c_=getchar();
    while(c_<'0'||c_>'9'){f_|=(c_=='-');c_=getchar();}
    while(c_>='0'&&c_<='9'){x_=(x_<<1)+(x_<<3)+(c_^48);c_=getchar();}
    x_=f_?-x_:x_;
}

inline void get_next() {
    int j=0;
    for(int i=2;i<=lb;i++) {
        while(j&&b[j+1]!=b[i]) j=next[j];
        if(b[j+1]==b[i]) ++j;
        next[i]=j;
    }    
}

inline void kmp() {
    int j=0;
    for(int i=1;i<=la;i++) {
        while(j&&a[i]!=b[j+1]) j=next[j];
        if(b[j+1]==a[i]) ++j;
        if(j==lb)
            printf("%d\n", i-j+1);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("luogu3375_kmp.in","r",stdin);
    freopen("luogu3375_kmp.out","w",stdout);
#endif
    scanf("\n%s%s", a+1, b+1); la=strlen(a+1), lb=strlen(b+1);
    get_next();
    kmp();
    for(int i=1;i<=lb;i++) printf("%d ", next[i]);
    puts("");
    return 0;
}
View Code

 


posted @ 2018-10-03 18:33  15owzLy1  阅读(200)  评论(0编辑  收藏  举报