zoukankan      html  css  js  c++  java
  • KMP算法总♂结

    讲KM♂P算法之前,我们先讲一个故♂事

    有一天,sgg给了老obo一封信和一个单词,并给他一个任务:找出这封信出现了多少个单词,然后在规定时间内告诉他。

    碰到这个问题,老obo会怎么做呢?

    首先最直观的想法是什么?

    就是先一维循环枚举信字符串的开头,再一维循环枚举单词的长度,一个一个判断是不是相同。这样的时间复杂度是O(n*m)的,n是信的长度,m是单词的长度。

    当n*m小的时候,可以在规定时间内出解,然而,要是n*m很大呢?

    我们想想有没有能优化以上算法的方法

    我们假设一个next数组,next[i]代表1~i部分的字符串,最长的前缀等于后缀是多长,注意,1~i字符串的长度不能作为next[i]存储的答案。

    这样有什么用呢?考虑这样:

    我们考虑对单词做出这个数组:next={0,0,0,1,0,0,1,2,0}(从0位置开始)

    来模拟一下我们一开始匹配的过程

     设定一个指针j,初始在0

    我们令i在信中从1扫描到8

    :1位置,a[i]==b[j+1],我们令j++

     2位置,a[i]==b[j+1],j++

     3位置,a[i]==b[j+1],j++

     4位置,a[i]==b[j+1],j++

     5位置,a[i]==b[j+1],j++

     6位置,a[i]==b[j+1],j++

     7位置,a[i]==b[j+1],j++

     8位置,a[i]!=b[j+1],那怎么办呢,如果让j直接等于0就相当于功亏一篑,于是我们让j=next[j],这样发现,之前匹配的s没有浪费掉,而且节约了时间。然后j=2,我们验证一下,在b中12位置的"sb"正好等于a中67位置的"sb"!

     9位置,a[i]==b[j+1],j++

    .........

    直到11位置,j==m (m为单词长度),这表明了匹配成功!

    那么我们怎么求这个next数组呢?

    先给出模板

    1 void kmp(){
    2     next[1]=0;int j=0;
    3     for (int i=2;i<=m;i++){
    4         while (j>0&&b[j+1]!=b[i]) j=next[j];
    5         if (b[j+1]==b[i]) j++;
    6         next[i]=j;
    7     }
    8 }

    就是这么简♂短,首先,next[1]=0,毋庸置疑,我们之前有声明过,next[i]不能等于1~i的长度。

    然后对于后面的i,我们已知了next[i-1]的值,我们先让j=next[i-1],

    如果b[j+1]=b[i],说明加上这个字符b[i],就等同于在原来相同的前缀后缀都增加了b[i]这个字符。

    如果不相同,那就让j=next[j],这个部分比较难理解,我们画张♂图

    假设这个字符串是单词,此时我的i已经到了9,j此时是4,当第10个字符出现的时候,如果它"失配"了,那么j=next[j],

    其中我们知道,next[4]=2,我们看一下,长度为2的"sx"也正好是这个字符串的前缀和后缀,这是因为,如果我当前的next值为4,那么说明我前缀后缀都包含了"sxsx"这个字符串,当我再度让j=next[j],也就是j从4变成2的时候,"sx"也刚好是他们的前缀后缀了!

    这样,O(m)计算next数组的方法就出来了。

    obo:那怎么和信匹配呢?

    给个模板:

     1 void match(){
     2     int j=0;
     3     for (int i=1;i<=n;i++){
     4         while (j>0&&b[j+1]!=a[i]) j=next[j];
     5         if (b[j+1]==a[i]) j++;
     6         if (j==m){
     7             printf("Yes!");
     8             return;
     9         }
    10     }
    11 }

    是不是很眼熟?没错,和处理next的程序很像,这是因为,kmp中处理next的过程正是"自我匹配"的过程,因为如果a能和b匹配,那a中也包含了b,所以匹配的过程本质是一样的。

  • 相关阅读:
    rabbitmq的安装和使用
    springboot之rabbitmq
    springboot之assembly的文件配置
    Android -- 真正的 高仿微信 打开网页的进度条效果
    香蕉云APP,2016下半年开发日记
    使用 Android Studio 检测内存泄漏与解决内存泄漏问题
    阿里云服务器上配置并使用: PHP + Redis + Mysql 从配置到使用
    PHP 获取 特定时间范围 类
    真实记录疑似Linux病毒导致服务器 带宽跑满的解决过程
    -Android -线程池 批量上传图片 -附php接收代码
  • 原文地址:https://www.cnblogs.com/qzqzgfy/p/5611248.html
Copyright © 2011-2022 走看看