zoukankan      html  css  js  c++  java
  • 单模式串匹配----浅谈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
    ", i-j+1);
        }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("luogu3375_kmp.in","r",stdin);
        freopen("luogu3375_kmp.out","w",stdout);
    #endif
        scanf("
    %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
    
    
  • 相关阅读:
    大型项目使用Automake/Autoconf完成编译配置
    用C语言编写Windows服务程序的五个步骤
    RPC的发展历史(本质就是双方定义好协议,传递参数后远程调用)
    libuv和libev 异步I/O库的比较
    zlog 程序日志的库 交叉编译(Linux生成ARM库,观察执行步骤)
    应用服务
    EvnetBus
    this指向
    CPU使用率
    数据量小,创建索引有必要吗
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/9740396.html
Copyright © 2011-2022 走看看