我也学算法 - KMP算法
2015-06-01 02:31
232 查看
一直以来,对算法都是理论大于实际,甚至没有实际.
最近由于项目需要.从新了解了一下KMP算法.唉,讨厌这种被动的学习过程.
不过KMP算法还是很有意思的,用了两天的时间才总算是弄懂了.期间参考了网上的博文和数据结构.下面分享一下KMP算法的心得.
KMP的总体思想是利用模式串本身的特性来优化匹配的步骤.如何利用自身的特性呢,KMP借助一个数组来实现,也就是大多数教程中提到的next数组.后面我会介绍next数组是如何构建和使用的.
前面提到KMP算法需要模式串满足一定的条件,那么这个条件是什么呢.这里直接引用数据结构书中的等式:
当k < j 时, t1t2…tk-1tk = tj-(k-1)tj-k… tj-1 tj.
如果没有这个等式,那么KMP算法无异于浪费了next大小的空间的最普通的字符串匹配算法.
下面用一张图来描述一下这个等式在匹配子串的时候起到的作用:
(本图片假定数组下标从1开始)首先我们找到等式: t1t2 = t6t7.所以当t8不匹配的时候,KMP算法会自动对齐t1 t2 然后用t3和母串进行匹配.
那么根据以上的分析,我们可以得出以下的几个结论(可能得出的有点仓促,同学们还是需要结合课本来进行理解)
1. next数组和模式串的下标是一一对应的.
2. 每个字符对应的next保存的是其前一个字符匹配的前缀的下标.从上面的图中可以看到next[8]保存的是3.
3. 第一个字母没有前缀,所以它的next保存的是一个无效的下标.在实际编程中,可以是-1
4. 如果一个字符的前面没有满足等式的子串,则其next保存的是模式串的首字母的下标.
根据我们的结论,下面给出KMP算法的实现:
KMP算法其实理解起来并不难,比较难的就是构造next数组.至少对我而言是这样.一旦数组构造出来,那剩下的就都好办了.
可能是我理解的还不够深把,写之前觉得有很多内容可以分享,但是开始写以后发现不知道该写什么.希望能够对同学们有所启发.
最近由于项目需要.从新了解了一下KMP算法.唉,讨厌这种被动的学习过程.
不过KMP算法还是很有意思的,用了两天的时间才总算是弄懂了.期间参考了网上的博文和数据结构.下面分享一下KMP算法的心得.
KMP的总体思想是利用模式串本身的特性来优化匹配的步骤.如何利用自身的特性呢,KMP借助一个数组来实现,也就是大多数教程中提到的next数组.后面我会介绍next数组是如何构建和使用的.
前面提到KMP算法需要模式串满足一定的条件,那么这个条件是什么呢.这里直接引用数据结构书中的等式:
当k < j 时, t1t2…tk-1tk = tj-(k-1)tj-k… tj-1 tj.
如果没有这个等式,那么KMP算法无异于浪费了next大小的空间的最普通的字符串匹配算法.
下面用一张图来描述一下这个等式在匹配子串的时候起到的作用:
(本图片假定数组下标从1开始)首先我们找到等式: t1t2 = t6t7.所以当t8不匹配的时候,KMP算法会自动对齐t1 t2 然后用t3和母串进行匹配.
那么根据以上的分析,我们可以得出以下的几个结论(可能得出的有点仓促,同学们还是需要结合课本来进行理解)
1. next数组和模式串的下标是一一对应的.
2. 每个字符对应的next保存的是其前一个字符匹配的前缀的下标.从上面的图中可以看到next[8]保存的是3.
3. 第一个字母没有前缀,所以它的next保存的是一个无效的下标.在实际编程中,可以是-1
4. 如果一个字符的前面没有满足等式的子串,则其next保存的是模式串的首字母的下标.
根据我们的结论,下面给出KMP算法的实现:
void kmp_get_next(char *pattern, int next[]) { int i = 0, j = 0; next[i] = -1; while (i < strlen(pattern) - 1) { if (j == i || pattern[i] == pattern[j]) { if (j != i) { j++; } i++; next[i] = j; } else if (j != 0) { j = next[j]; } else { i++; } } } void kmp_print_next(char *pattern, int next[]) { int i = 0; if (NULL == pattern) { return; } printf("%s 's next array is : \n", pattern); for (i = 0; i < strlen(pattern); i++) { printf("%d\t", next[i]); } } int kmp_is_match(char *pattern, char *basestr, int next[]) { int i = 0; int j = 0; while (i < strlen(basestr)) { if (basestr[i] == basestr[j]) { if (j == strlen(pattern)) { return 0; } i++; j++; } else { j = next[j]; if (j == -1) { i++; } } } return -1; } int kmp_get_match(char *pattern, char *basestr, int next[]) { int i = 0; int j = 0; while (i < strlen(basestr)) { if (basestr[i] == basestr[j]) { if (j == strlen(pattern)) { return i; } i++; j++; } else { j = next[j]; if (j == -1) { i++; } } } return -1; }
#include "kmp.h" int main(int argc, char **argv) { int ret = 0; int i = 0; char *pattern = "abaabc"; int next[10] = {0}; char *basestr = "abcabcabcabccacabdabaabcabceadb"; kmp_get_next(pattern, next); printf("%s matched\n", kmp_is_match(pattern, basestr, next) == 0 ? "is" : "not"); ret = kmp_get_match(pattern, basestr, next); return 0; }
KMP算法其实理解起来并不难,比较难的就是构造next数组.至少对我而言是这样.一旦数组构造出来,那剩下的就都好办了.
可能是我理解的还不够深把,写之前觉得有很多内容可以分享,但是开始写以后发现不知道该写什么.希望能够对同学们有所启发.
相关文章推荐
- CoreData 多线程处理大量数据同步时的操作
- 教你开通超级QQ,
- C++对象模型之复制构造函数的构造操作
- 作为第一期iOS基础班学生,MJ广东开校之我见(原稿)
- java 打印出两个IP之间的所有IP值
- 自己的路删除
- YouCompleteMe实现vim自动补全
- ubunu下用命令设置壁纸
- 程序员、编程常用术语
- Mastering "Product Flavors" on Android
- 申请Bing Search API
- C语言程序设计笔记(二)
- 就着近事儿,谈一谈游戏数值策划的面试
- sql 邮件发送测试情况
- linux进程控制
- linux进程控制
- java实现链表
- 浅谈HashMap以及重写hashCode()和equals()方法
- 【助教】Java获取数据库数据展示
- javascript模板引擎原理