zoukankan      html  css  js  c++  java
  • KMP算法

    注明:参考文献《信息学奥赛一本通》

    ---------------------------------------------------------------------------------------------------------------------"my name is the porter of nature"

    介绍

    KMP算法是用于字符串匹配问题的,它利用一种巧妙而又不失逻辑的方法去减少算法的时间复杂度,在

    理较多数据匹配时或者数据范围大的时候用处极大(反正我是五体投地),也就是如果问主串是否包含

    串的问题可以大幅度让你开心得飞起...

    举个栗子 :我们有一个主串--- A=”aaaaaaaaaaaaaaaaab" 和 子串--- B=“aaab”,如果我们要找从A串中哪

    一个位置起开始匹配,通常是枚举位置后验证匹配,那么它的时间复杂度为O(n*m)[n为A串长,b为B串长]

    ,最坏情况下这通常不能接受,大大的TLE就给你奉上;而KMP算法即使是最坏情况,它也可以满足O(n)的

    时间复杂度。

    概念

    首先我们明确几个概念。。。

    A=”aaaaaaaaaaaaaaaaab"    B=“aaab”

    ~主串(母串)——即等待匹配的A串

    ~模式串——即用来匹配的B串

    流程

    假设我们现在有一个主串A=“abababaabab”和一个模式串B=“ababacb”,我们用实例去表示这个过程:

    我们现在匹配到i=j=5时,此时前面的都可以匹配,但是A[6]!=B[6].

    i= 1 2 3 4 5 6 7 8 9 ...

        a b a b a b a a b a b ...          (主串)

        a b a b a c b                          (模式串)

    j= 1 2 3 4 5 6 7

    于是我们就用你聪明的小脑瓜子想一下,我们现在应该将j改成更小的j’,使得在B[1......j]中前面j‘个字母可以满足刚好

    和A串前面的匹配,然后继续匹配下去。这个时候我们可以发现,B[1......j]中前面j’个字母和末尾的j'个字母

    要完全相同,(如aba==aba)这样才能移动B串,如下面所示:

    i= 1 2 3 4 5 6 7 8 9 ...

        a b a b a b a a b a b ...          (主串)

              a b a b a c b                    (模式串)

    j=       1 2 3 4 5 6 7


    这样的话我们就可让A[6]=B[4]了,即可以继续匹配下去。我们也可以得知,新的j的取值与i是没有关系

    的,它就是一个B串起点在疯狂跳跃的过程,逆向思考的话,就是在前面的j对应的字母不能匹配的话,

    就缩小j,使得B串能够跳跃去继续匹配罢了,,

    那么我们可以预处理一个数组P[j]去处理,即它表示B组与A组匹配到B组第j个字母,第j+1个字母不能

    匹配时,新的j最大可以是多少。则P[j]满足B[1......k] = B[j-k+1......j](好好消化一下再继续看)

     

    此时我们继续匹配下去,则有:

    i= 1 2 3 4 5 6 7 8 9 ...

        a b a b a b a a b a b ...          (主串)

              a b a b a c b                    (模式串)

    j=       1 2 3 4 5 6 7

    这时又出现A[7+1]!=B[5+1],我们就让B开始跳起来,因为新的 j = P[5] = 3;(P的求法后续再讲)

    i= 1 2 3 4 5 6 7 8 9 ...

        a b a b a b a a b a b ...          (主串)

                    a b a b a c b              (模式串)

    j=             1 2 3 4 5 6 7

     但是a != b ,所以我们 继续 j = P[3] = 1;

    i= 1 2 3 4 5 6 7 8 9 ...

        a b a b a b a a b a b ...          (主串)

                          a b a b a c b       (模式串)

    j=                   1 2 3 4 5 6 7

    依旧是不满足,那就 j = P[1] = 0了,它已经没力气了~~

    i= 1 2 3 4 5 6 7 8 9 ...

        a b a b a b a a b a b ...          (主串)

                             a b a b a c b     (模式串)

    j=                   0 1 2 3 4 5 6 7

    所以我们只有j==m的时候,我们才可以匹配完全...但也存在后续匹配不了滴~

    先打这个匹配过程的代码?

    j=0; 
    for(i=0;i<sA.length();i++){
        while(j>0 && sB[j+1]!=sA[i+1]) j=P[j];
        //即不能继续匹配下去,且还没到没力气的时候,就继续减小j
        if(sB[j+1]==sA[i+1]) j++; 
        //此时可以继续匹配,那就匹配下去
        if(j==m) {
        printf("%ld ",i+1-m+1);// 子串在母串的位置 ,当然是第几个位置喽
        j=p[j];   //开启新征程
        }
    }
    

     敲黑板,划重点

    P数组也可以用类似的方式求解,避免预处理时间复杂度达到m2--m3,但就是P数组处理是一个自我

    匹配的过程罢了,复杂度O(m)

          1 2 3 4 5 6 7

    B= a b a b a c b

    P= 0 0 1 2 ? ? ?

    此时我们不难发现,P[5] = P[4]+1; 因为由于P[4] = 2 ,则 有B [1...2] = B [ 3...4],而因为B[3] = B[5],

    所以我们就在P[4]上+1,得到P[5];

    但是显然,P[6]无法通过P[5] 得到,因为B[6]无法跟B[4]相等,那么我们或许可以继续推下去,如果

    P[P[5]]=P[3]=1可以呢,也就是说或许B[2]=B[6]呢?

    然而依旧不行,所以我们就继续下去,得到P[6]=0;

    自我匹配代码如下:

    P[1]=0; 
    j=0;
    for(long i=1;i<m;i++){  
        while(j>0 && B[j+1]!=B[i+1]) j=P[j];
        if(B[j+1]==B[i+1]) j++;
        P[i+1]=j;  //将i+1位置的字符一一赋值
    }
    

     码了2个小时,累死我了。。。


    当然也要学会运用,比如刷刷题?

     对了注意一点字符串处理的时候,应将其从1开始

    ...
    char B[1010];
    scanf("%s",B+1);
    ...
    

      

     

  • 相关阅读:
    《图解算法》读书笔记(十)K最近邻算法
    《图解算法》读书笔记(九) 动态规划
    《图解算法》读书笔记(八) 贪婪算法
    Go 常用包之fmt、flag包(四)
    GO环境及初始化项目(二)
    nginx fpm 常见错误对比分析
    Ueditor富文本添加、编辑视频,视频不显示解决方案
    phpunit 测试
    mysql 主从并行复制(MTS)
    Explain执行计划详解
  • 原文地址:https://www.cnblogs.com/yuzhe123/p/13308395.html
Copyright © 2011-2022 走看看