zoukankan      html  css  js  c++  java
  • 【初识】KMP算法入门

    举个例子

    模式串S:a s d a s d a s d f a s d

    匹配串T:a s d a s d f

    如果使用朴素匹配算法——

    1 2 3 4 5 6 7 8 9 

    a s d a s d a s d f a s d

    a s d a s d f

    1 2 3 4 5 6 7

    此时,匹配到了S7和T7了,S7为a而T7为f,不匹配那么朴素的匹配算法会这么做——

    2 3 4 5 6 7 8 9 

    s d a s d a s d f a s d

      a s d a s d f

      2 3 4 5 6 7

    这时,我们会发现,模式串回溯到了S2,而匹配串回溯到了T1。

    很明显,这会极大的降低算法的效率,在最坏情况下,我们需要将模式串几乎每个元素都查询一次,而每次查询都从匹配串的串首走到接近串尾,这样的时间复杂度为n*m,其中n和m分别为模式串和匹配串的长度。

    那么我们是否有可能降低时间复杂度呢?答案是肯定的——很明显我们只需要想办法减少回溯,就可以达到效果。Kmp算法就是使用这种方法节省时间的。

    1 2 3 4 5 6 7 8 9 

    a s d a s d a s d f a s d

    a s d a s d f

    1 2 3 4 5 6 7

    这个东西很熟悉吧?刚刚出现过一次。

    那么,kmp算法会怎么执行下一步呢?答案如下——

    1 2 3 4 5 6 7 8 9 

    a s d a s d a s d f a s d

            a s d a s d f

            1 2 3 4 5 6 7

    注意这一步!这里的模式串根本没有回溯,只是将匹配串向后移动了若干步。这样,最坏情况只是将模式串走一遍,然后将匹配串走一遍,当然了,匹配串里面的部分元素会走多次,但是,很明显这种算法会将n*m降低到n+k,这个k和m内部部分元素的重复次数有关,最大不会超过n(当然这是我自己证明得到的,不一定正确,以后我还会继续证明的)。

    好了,方法知道了,那么怎么实现呢?

    换句话说,怎么实现迅速的移动匹配串呢?答案是——添加一个Next数组,标记匹配串中的特性。

    这个Next数组的特性很明显

    1. Next[0] = -1,即这是第一个元素,前面没有可以替换它的。
    2. Next[j] = k ; { k | T[0] = T[j-k], T[1] = T[j-k+1],... , T[k-1] = T[j-1]}。
    3. Next[j] = 0; 其他情况。

    举例:

    匹配串T: a  s  d  a  s  d  f

    Next:   -1  0  0  0  1  2  3

    具体见代码——

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 #include <algorithm>
     6 using namespace std;
     7 
     8 const int N = 2010;
     9 
    10 char s[N], t[N];
    11 int Next[N];
    12 int lenS, lenT;
    13 
    14 void kmpNext(char* T)                                           //计算Next数组
    15 {
    16     int i = 1;
    17     Next[0] = -1;                                               //Next[0] = -1
    18     while(i < lenT)
    19     {
    20         int j = 0;
    21         while(T[j] == T[i])                                     //Next[i] = j; { j | T[0] = T[i-j], T[1] = T[i-j+1],... , T[j-1] = T[i-1]}
    22         {
    23             Next[i] = j;
    24             i++;
    25             j++;
    26         }
    27         Next[i] = j;                                            //同上,或等于0
    28         i++;
    29     }
    30 }
    31 
    32 bool kmp(char* S, char* T)                                      //kmp
    33 {
    34     lenS = strlen(S);
    35     lenT = strlen(T);
    36     kmpNext(T);
    37     int i = 0, j = 0;
    38     while(i < lenS && j < lenT)                                 //当模式串或匹配串走完时退出
    39     {
    40         if(j == -1)
    41         {
    42             i++;
    43             j = 0;
    44         }
    45         else if(S[i] == T[j])
    46         {
    47             i++;
    48             j++;
    49         }
    50         else j = Next[j];
    51     }
    52     if(j == lenT) return 1;                                     //如果匹配串走完,表示匹配串是模式串的子串
    53     return 0;
    54 }
    55 
    56 int main()
    57 {
    58     //freopen("test.in", "r", stdin);
    59     while(~scanf("%s%s", s, t))
    60     {
    61         if(kmp(s, t)) printf("Yes
    ");
    62         else printf("No
    ");
    63     }
    64     return 0;
    65 }
    View Code
  • 相关阅读:
    20201120-1 每周例行报告
    20201112-1 每周例行报告
    20201105-1 每周例行报告
    20201029-1 每周例行报告
    20201022-1 每周例行报告
    20201015-3 每周例行报告
    20201008-1 每周例行报告
    20200924-1 每周例行报告
    使用代理服务器的方式在一个端口上运行多个服务
    只出现一次的数字
  • 原文地址:https://www.cnblogs.com/mypride/p/4950245.html
Copyright © 2011-2022 走看看