KMP 算法,俗称“看毛片”算法,是字符串匹配中的很强大的一个算法,不过,对于初学者来说,要弄懂它确实不易。

笔者认为,KMP 算法之所以难懂,很大一部分原因是很多实现的方法在一些细节的差异。体现在几个方面: next 数组,有的叫做“失配函数”,其实是一个东西; next 数组中,有的是以下标为 0 开始的,有的是以 1 开始的; KMP 主算法中,当发生失配时,取的 next 数组的值也不一样!就这样,各说各的,乱的很!

所以,在阐述我的理解之前,我有必要说明一下,我是用 next 数组的, next 数组是以下标 0 开始的!还有,我不会在一些基础的概念上浪费太多,所以你在看这篇文章时必须要懂得一些基本的概念,例如 “ 朴素字符串匹配 ”、“ 前缀 ” , “ 后缀 ” 等!

假设在我们的匹配过程中出现了这一种情况:

根据 KMP 算法,在该失配位会调用该位的 next 数组的值!在这里有必要来说一下next 数组的作用!说的太繁琐怕你听不懂,让我用一句话来说明:

返回失配位之前的最长公共前后缀!

好,不管你懂不懂这句话,我下面的文字和图应该会让你懂这句话的意思以及作用的!

首先,我们取之前已经匹配的部分(即蓝色的那部分!)

我们在上面说到 next 数组的作用时,说到 “ 最长公共前后缀 ” ,体现到图中就是这个样子!

接下来,就是最重要的了!

没错,这个就是 next 数组的作用了 :

返回当前的最长公共前后缀长度,假设为 len 。因为数组是由 0 开始的,所以 next数组让第 len 位与主串匹配就是拿最长前缀之后的第 1 位与失配位重新匹配,避免匹配串从头开始!如下图所示!

(重新匹配刚才的失配位!)

如果都说成这样你都不明白,那么你真的得重新理解什么是 KMP 算法了!

接下来最重要的,也是 KMP 算法的核心所在,就是 next 数组的求解!不过,在这里我找到了一个全新的理解方法!如果你懂的上面我写的的,那么下面的内容你只需稍微思考一下就行了!

跟刚才一样,我用一句话来阐述一下 next 数组的求解方法,其实也就是两个字:

继承

a 、当前面字符的前一个字符的对称程度为 0 的时候,只要将当前字符与子串第一个字符进行比较。这个很好理解啊,前面都是 0 ,说明都不对称了,如果多加了一个字符,要对称的话最多是当前的和第一个对称。比如 agcta 这个里面 t 的是 0 ,那么后面的 a 的对称程度只需要看它是不是等于第一个字符 a 了。

b 、按照这个推理,我们就可以总结一个规律,不仅前面是 0 呀,如果前面一个字符的 next 值是 1 ,那么我们就把当前字符与子串第二个字符进行比较,因为前面的是1 ,说明前面的字符已经和第一个相等了,如果这个又与第二个相等了,说明对称程度就是 2 了。有两个字符对称了。比如上面 agctag ,倒数第二个 a 的 next 是 1 ,说明它和第一个 a 对称了,接着我们就把最后一个 g 与第二个 g 比较,又相等,自然对称成都就累加了,就是 2 了。

c 、按照上面的推理,如果一直相等,就一直累加,可以一直推啊,推到这里应该一点难度都没有吧,如果你觉得有难度说明我写的太失败了。

当然不可能会那么顺利让我们一直对称下去,如果遇到下一个不相等了,那么说明不能继承前面的对称性了,这种情况只能说明没有那么多对称了,但是不能说明一点对称性都没有,所以遇到这种情况就要重新来考虑,这个也是难点所在。

如果蓝色的部分相同,则当前 next 数组的值为上一个 next 的值加一,如果不相同,就是我们下面要说的!

如果不相同,用一句话来说,就是:

从前面来找子前后缀

1 、如果要存在对称性,那么对称程度肯定比前面这个的对称程度小,所以要找个更小的对称,这个不用解释了吧,如果大那么就继承前面的对称性了。

2 、要找更小的对称,必然在对称内部还存在子对称,而且这个必须紧接着在子对称之后。

如果不能理解,那么看一下图吧!

好了,我已经把该说的尽可能以最浅显的话和最直接的图展示出来了,如果还是不懂,那我真的没有办法了!

说了这么多,下面是代码实现

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #define N 100

 void cal_next( char * str, int * next, int len )
 {
     int i, j;

     next[] = -;
     ; i < len; i++ )
     {
         j = next[ i -  ];
          ] != str[ i ] && ( j >=  ) )
         {
             j = next[ j ];
         }
          ] )
         {
             next[ i ] = j + ;
         }
         else
         {
             next[ i ] = -;
         }
     }
 }

 int KMP( char * str, int slen, char * ptr, int plen, int * next )
 {
     , p_i = ;

     while( s_i < slen && p_i < plen )
     {
         if( str[ s_i ] == ptr[ p_i ] )
         {
             s_i++;
             p_i++;
         }
         else
         {
              )
             {
                 s_i++;
             }
             else
             {
                 p_i = next[ p_i -  ] + ;
             }
         }
     }
     ;
 }

 int main()
 {
     };
     };
     int slen, plen;
     int next[ N ];

     while( scanf( "%s%s", str, ptr ) )
     {
         slen = strlen( str );
         plen = strlen( ptr );
         cal_next( ptr, next, plen );
         printf( "%d\n", KMP( str, slen, ptr, plen, next ) );
     }
     ;
 }

最新文章

  1. 给你的应用“一只”智慧的眼睛 —— Barcode常识普及以及识别信息处理
  2. 区间第K大(一)
  3. UVa12264 Risk(最大流)
  4. JS控制DIV隐藏显示
  5. CreateFeatureClass 异常,尝试读取或写入受保护的内存 Access
  6. 设计模式之原型模式(Prototype)
  7. 反射 介绍System.Type类
  8. 对于java用发送http请求,请求内容为xml格式
  9. Mysql集群读写分离(Amoeba)
  10. spring非controller类获取service方法
  11. 《Java从入门到放弃》JavaSE入门篇:练习——单身狗租赁系统
  12. centos6.5上进行crontab操作
  13. SpringMVC 使用PUT请求遇到的问题小结
  14. notepad++使用收集
  15. zabbix 自定义监控 排除带报错提示
  16. 【java初探外篇01】——关于Java修饰符
  17. tms web core pwa让你的WEB APP离线可用
  18. 机器学习理论基础学习14.1---线性动态系统-卡曼滤波 Kalman filter
  19. springmvc jar包下载 提供地址
  20. CentOS7 安装lua环境(我是在mysql读写分离用的)

热门文章

  1. 7. LAMP环境搭建
  2. wireshark常用过滤条件
  3. cmd 下切换目录
  4. android Json Gson FastJson 解析
  5. 通过js引用外部脚本(嘿嘿,方便直接在浏览器上调试抓取代码)
  6. Linux关机和重启命令
  7. leetcode题目清单
  8. 淘宝网触屏版 - 学习笔记(0 - 关于dpr)
  9. LTE 测试文档(翻译)
  10. LMAX Disruptor—多生产者多消费者中,消息复制分发的高性能实现