数据结构—KMP

KMP算法用于解决两个字符串匹配的问题,但更多的时候用到的是next数组的含义,用到next数组的时候,大多是题目跟前后缀有关的 。

首先介绍KMP算法:(假定next数组已经学会,后边next数组会在介绍)

上图T为主链,P为模板链,要求P在T中是否出现,出现就返回位置。

朴素算法会顺序遍历,比较第一次的时候p[0]处失配,然后向后移动继续匹配。数据量大的时候这么做肯定是不可行的。所以这里就会有KMP算法!在一次失配之后,KMP算法认为这里已经失配了,就不能在比较一遍了,而是将字符串P向前移动(已匹配长度-最大公共长度)位,接着继续比较下一个位置。这里已匹配长度好理解,但是最大公共长度是什么呐?这里就出现了next数组,next数组:next[i]表示的是P[0-i]最大公共前后缀公共长度。这里肯定又有人要问了,next数组这么奇葩的定义,为什么就能算出来字符串需要向后平移几位才不会重复比较呐?

上图中红星标记为例,此时在p[4]处失配,已匹配长度为4,而next[3]=2(也就是babaa中前后缀最大公共长度为0),这时候向后平移已匹配长度-最大公共长度=2位,P[0]到达原来的P[2]的位置,如果只平移一位,P[0]到达p[1]的位置这个位置没有匹配这次操作就是无用功所以浪费掉了时间。已知前缀后缀中的最大公共长度,下次位移的时候直接把前缀位移到后缀上面直接产生匹配,这样直接从后缀的后一位开始比较就可以了。这样将一下无意义的位移过滤掉剩去了不少的时间。

下面讲解next数组通过语言进行实现:

  1. void makeNext(const char P[],int next[])
  2. {
  3.     int q,k;
  4.     int m=strlen(P);
  5.     next[]=;
  6.     for (q=,k=;q<m;++q)
  7.     {
  8.         while(k>&&P[q]!=P[k])
  9.             k = next[k-];
  10.         /*
  11.         这里的while循环很不好理解!
  12.         就是用一个循环来求出前后缀最大公共长度;
  13.         首先比较P[q]和P[K]是否相等如果相等的话说明已经K的数值就是已匹配到的长的;
  14.         如果不相等的话,那么next[k-1]与P[q]的长度,为什么呐?因为当前长度不合适
  15.         了,不能增长模板链,就缩小看看next[k-1]
  16.         的长度能够不能和P[q]匹配,这么一直递归下去直到找到
  17.         */
  18.         if(P[q]==P[k])//如果当前位置也能匹配上,那么长度可以+1
  19.         {
  20.             k++;
  21.         }
  22.         next[q]=k;
  23.     }
  24. }

上面KMP算法的理论部分已经讲解完了,下面解释语言实现:

  1. int kmp(const char T[],const char P[],int next[])
  2. {
  3.     int n,m;
  4.     int i,q;
  5.     n = strlen(T);
  6.     m = strlen(P);
  7.     makeNext(P,next);
  8.     for (i=,q=;i<n;++i)
  9.     {
  10.         while(q>&&P[q]!= T[i])
  11.             q = next[q-];
  12.         /*
  13.         这里的循环就是位移之后P的前几个字符能个T模板匹配
  14.         */
  15.         if(P[q]==T[i])
  16.         {
  17.             q++;
  18.         }
  19.         if(q==m)//如果能匹配的长度刚好是T的长度那么就是找到了一个能匹配成功的位置
  20.         {
  21.             printf("Pattern occurs with shift:%d\n",(i-m+));
  22.         }
  23.     }
  24. }
  25.  

另外KMP算法还可以进一步的优化:

  1. /*************************KMP模板****************************/
  2. int next[];//优化后的失配指针,记住这里next要比P多一位,因为P到m-1即可,但是next还要计算出m的失配指针
  3. int next2[];//next2用来保存KM指针,是为优化next的失配指针,next保存的是优化之后的失配指针
  4. char T[];//待匹配串
  5. char P[];//模板串
  6. void makeNext(char *P, int *next)
  7. {
  8.     int m = strlen(P);
  9.     next[]=next[]=;
  10.     next2[]=next2[]=;
  11.     for(int i=;i<m;i++)
  12.     {
  13.         int j = next2[i];
  14.         //这里直接找出当前位置上一步的next,和上一步不断保存K值是一个道理
  15.         while(j && P[i]!=P[j])
  16.             j = next2[j];
  17.         next2[i+]=next[i+]=(P[i]==P[j])?j+:;
  18.  
  19.         //既然i+1的失配位置指向j+1,但是P[i+1]和P[j+1]的内容是相同的
  20.         //所以就算指针从i+1跳到j+1去,还是不能匹配,所以next[i+1]直接=next[j+1]
  21.         if(next[i+]==j+ && P[i+]==P[j+]) //这一步就是进行优化,如果下一个位置还能和当前位置匹配那么直接更新next数组的值
  22.             next[i+]=next[j+];
  23.     }
  24. }
  25. void kmp(char *T, char *P, int *next) //找到所有匹配点
  26. {
  27.     int n = strlen(T);
  28.     int m = strlen(P);
  29.     int j = ;
  30.     for(int i = ; i < n; i++)
  31.     {
  32.         while(j && T[i] != P[j]) j = next[j];//向前移动了多少
  33.         inext(T[i] == P[j]) j++;
  34.         inext(j == m) printnext("%d\n", i - m + );
  35.     }
  36. }
  37. /*************************KMP模板****************************/

扩展KMP算法

这里稍稍的提一点,时间仓促,我也还没有彻底的理解……啧啧啧

理论部分如果我讲的不好别喷,求T与S[i,n-1]的最长公共前缀extend[i],要求出所有extend[i](0<=i<n)。下面从模板中讲解:

  1. const int maxn=;   //字符串长度最大值
  2. int next[maxn],ex[maxn]; //ex数组即为extend数组
  3. /*
  4. extend数组,extend[i]表示T与S[i,n-1]的最长公共前缀,要求出所有extend[i](0<=i<n)。
  5. */
  6.  
  7. /*
  8. 设辅助数组next[i]表示T[i,m-1]和T的最长公共前缀长度
  9. */
  10.  
  11. //预处理计算next数组
  12. void GETNEXT(char *str)
  13. {
  14.     int i=,j,po,len=strlen(str);
  15.     next[]=len;//初始化next[0]
  16.     /*
  17.     0到n-1组成的字符串和str的最长公共前缀长度当然是len了
  18.     */
  19.     while(str[i]==str[i+]&&i+<len)//计算next[1],也就是第一位的时候能匹配多少
  20.     i++;
  21.     next[]=i;
  22.  
  23.     po=;//初始化po的位置
  24.     for(i=;i<len;i++)
  25.     {
  26.         if(next[i-po]+i<next[po]+po)//第一种情况,可以直接得到next[i]的值
  27.         /*
  28.         如果不如之前计算过的最长的长就直接赋值为最长的那个
  29.         */
  30.         next[i]=next[i-po];
  31.         else//第二种情况,要继续匹配才能得到next[i]的值
  32.         /*
  33.         比最长的还短,那么后面的就不是到了,所以要继续匹配
  34.         */
  35.         {
  36.             j=next[po]+po-i;
  37.             if(j<)j=;//如果i>po+next[po],则要从头开始匹配
  38.             while(i+j<len&&str[j]==str[j+i])//计算next[i]
  39.             j++;
  40.             next[i]=j;
  41.             po=i;//更新po的位置
  42.         }
  43.     }
  44. }
  45. //计算extend数组
  46. void EXKMP(char *s1,char *s2)
  47. {
  48.     int i=,j,po,len=strlen(s1),l2=strlen(s2);
  49.     GETNEXT(s2);//计算子串的next数组
  50.     while(s1[i]==s2[i]&&i<l2&&i<len)//计算ex[0]
  51.     i++;
  52.     ex[]=i;
  53.     po=;//初始化po的位置
  54.     for(i=;i<len;i++)
  55.     {
  56.         if(next[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值
  57.         ex[i]=next[i-po];
  58.         else//第二种情况,要继续匹配才能得到ex[i]的值
  59.         {
  60.             j=ex[po]+po-i;
  61.             if(j<)j=;//如果i>ex[po]+po则要从头开始匹配
  62.             while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]
  63.             j++;
  64.             ex[i]=j;
  65.             po=i;//更新po的位置
  66.         }
  67.     }
  68. }

数据结构--KMP算法总结的更多相关文章

  1. 实验数据结构——KMP算法Test.ming

    翻译计划     小明初学者C++,它确定了四个算术.关系运算符.逻辑运算.颂值操作.输入输出.使用简单的选择和循环结构.但他的英语不是很好,记住太多的保留字,他利用汉语拼音的保留字,小屋C++,发明 ...

  2. 数据结构——KMP算法

    算法介绍 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法).KMP算法的核心是利用 ...

  3. 数据结构-kmp算法

    定义 改进字符串的匹配算法 关键:通过实现一个包含了模式串的局部匹配信息的next()函数,利用匹配失败的信息,减少匹配次数. 1.BF算法 暴力匹配 给定 文本串S "BBC ABCDAB ...

  4. <数据结构>KMP算法

    next数组 定义 严格定义:next[i]表示使子串s[0...k] == s[i-k...i]的最大的k(前后缀可以重叠,但不能是s[0..i]本身) 含义:最长相等前后缀的下标,没有则赋-1 图 ...

  5. 大话数据结构——KMP算法(还存在问题)

    http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html /*#include& ...

  6. 数据结构KMP算法中手算next数组

    总结一下今天的收获(以王道数据结构书上的为例子,虽然我没看它上面的...):其中竖着的一列值是模式串前缀和后缀最长公共前缀. 最后求得的结果符合书上的结果,如果是以-1开头的话就不需要再加1,如果是以 ...

  7. 数据结构- 串的模式匹配算法:BF和 KMP算法

      数据结构- 串的模式匹配算法:BF和 KMP算法  Brute-Force算法的思想 1.BF(Brute-Force)算法 Brute-Force算法的基本思想是: 1) 从目标串s 的第一个字 ...

  8. 数据结构与算法--KMP算法查找子字符串

    数据结构与算法--KMP算法查找子字符串 部分内容和图片来自这三篇文章: 这篇文章.这篇文章.还有这篇他们写得非常棒.结合他们的解释和自己的理解,完成了本文. 上一节介绍了暴力法查找子字符串,同时也发 ...

  9. 【数据结构】KMP算法

    我还是不太懂... 转2篇大神的解释    1>https://www.cnblogs.com/yjiyjige/p/3263858.html     2>https://blog.csd ...

随机推荐

  1. 【SQL】- 基础知识梳理(六) - 游标

    游标的概念 结果集,结果集就是select查询之后返回的所有行数据的集合. 游标(Cursor): 是处理数据的一种方法. 它可以定位到结果集中的某一行,对数据进行读写. 也可以移动游标定位到你需要的 ...

  2. angular $compiler

    directive是如何被compiled HTML编译发生在三个阶段: 1.$compile遍历DOM节点匹配directives 如果compiler找到元素上的directive,directi ...

  3. 教育,创新,提升:Indiegogo和Kickstarter上受中国用户支持的10个众筹项目

    中国的经济正在迅速发展,已成为世界第二大经济体.中国家庭随着经济水平的提高,越来越多父母愿意将自己的子女送到海外留学. 家长们希望自己的子女可以有机会接受国外大学优质的教育, 以便他们将来可以学成归来 ...

  4. Spark官方2 ---------Spark 编程指南(1.5.0)

    概述 在高层次上,每个Spark应用程序都由一个运行用户main方法的driver program组成,并在集群上执行各种 parallel operations.Spark提供的主要抽象是resil ...

  5. UI自动化测试(三)对页面中定位到的元素对象做相应操作

    前两天分别讲述了UI自动化测试基础以及对页面元素该如何进行定位,这一篇自然就是对定位到的页面元素对象进行相应操作啦. 阅读目录 1.常用操作元素对象的方法 2.鼠标事件操作 3.键盘事件操作 4.We ...

  6. Array Partition I

    Given an array of 2n integers, your task is to group these integers into n pairs of integer, say (a1 ...

  7. Centos7 创建本地 docker 仓库极其遇到的问题

    环境安装: VirtualBox 安装 Centos7 安装 docker 1. 配置私有仓库和客户端地址 私有仓库:192.168.1.104 客户端:192.168.1.103 通过 Centos ...

  8. httpd日志和日志轮替工具

    html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...

  9. windows phone 模拟器

    window phone 模拟器启动报错 修改Bios设置,我的是yoga pro 2,只修改 即可.启动成功

  10. HDU1789 Doing Homework again

    基础单调队列: #include<cstdio> #include<cstdlib> #include<iostream> #include<algorith ...