1. 概述

KMP算法是Knuth、Morris、Pratt在1977年提出的一种基于前缀的匹配算法。该算法通过预处理模式计算得到前缀函数π,π包含有模式自身位移进行匹配的信息,这些信息可避免朴素字符串搜索中的无用位移测试,安全的一次步进多个位移,进而减少匹配次数。KMP算法的核心是计算得到前缀函数π,得到π函数之后的搜索过程,反而显得很简单。,KMP算法总体具有线性时间复杂度,其中预处理阶段为 O(m),搜索阶段的最坏和平均时间复杂度均为 O(n),总时间复杂度为 O(n+m)。

2. 前缀函数 Π

先假设已经读入Text[i..i+k] 处长度为k (k>=0) 的字符串u与模式串前缀匹配,并且文本字符串i+k+1处字符α与模式字符串k+1处字符β失配。如下图Figure-01所示。

此时最简单的处理方案是将搜索窗口向右移动一位,再次从模式串第一个字符开始比对。Brute Search便是采用上述方法实现,类似这样的方式没有充分利用已经匹配过的字符信息。KMP算法通过预先处理模式,可以充分利用已成功匹配的信息,减少匹配次数,其预处理时间复杂度为O(m),搜索阶段时间复杂度为O(n),总时间复杂度为O(m+n)。

KMP算法的原始思想源于Morris and Pratt的论文 [MP70],如下(参考图Figure-02):
    • 对于模式的每个前缀u预先计算最长边界b(u)。
    • 在当前文本位置,设u是p的最长前缀,同时也是t1…ti的后缀。读入文本字符σ = ti+1。如果σ = p∣u∣+1,此时最长前缀是up∣u∣+1。但是如果σ ≠ p∣u∣+1,比较σ与p∣b(u)∣+1,如果σ = p∣b(u)∣+1,则b(u)p∣b(u)∣+1是新的p的最长前缀,同时也是t1…ti ti+1的后缀。如果σ ≠ p∣b(u)∣+1,比较σ = p∣b(b(u))∣+1,并循环,直到σ的一个边界或者没有任何边界(空边界),此时新的最长前缀为空字符串。

Knuth提出了一个改进。如果σ = ti+1和p∣u∣+1匹配失败,则u的边界的任何后续字符不能等于p∣u∣+1否则和σ不匹配。因此在预处理阶段,对于p的每个真前缀u (p = uw, w ≠ ∊),可以预先计算最长边界v使得p∣u∣+1 ≠ p∣v∣+1。(如同中所示即为γ≠β。)
我们可以用数学语言描述前缀函数π[q] = max { k: k < q and pk⊐ pq }
可见,为了得到前缀函数π,需要对于模式P的每个前缀u计算最长边界max{b(u)},使得p∣u∣+1 ≠ p∣v∣+1。当逐字符读入模式p1…pm,并计算p1…pi的最长边界时,事实上需要计算的最长后缀pj…pi (1≤j≤i) 也是p的前缀。因此,我们可以将KMP算法应用于其本身。KMP的预处理阶段可由KMP来完成,其时间复杂度为O(m) 。

3. 匹配过程

得到前缀函数π之后,从左到右扫描文本串T进行匹配。假定当前P[1..j]和T[i-j..i]匹配,继续检测T[i+1]和P[j+1],此时分两种情况:
    • T[i+1]=P[j+1]。令i=i+1,j=j+1。如果j等于模式串长度,则匹配成功返回;如果i等于文本串长度,则匹配失败返回;否则 继续执行下次比较。
    • T[i+1]≠P[j+1]。按照如下循环过程执行匹配检测(循环结束后,让模式串的位置标记j=k):
      1. 令k=π[j],如果k=1则结束循环。
      2. 由前缀函数的特性,可知P[1..k]⊐P[1..j]。由于T[i-j..i]和P[1..j]匹配,因此P[1..k]和T[i-k..i]匹配,可以直接进行P[k+1]和T[i+1]的匹配检测。
      3. 如果P[k+1]= T[i+1],则结束循环;否则继续执行1。
      如下Figure-03展示了某此失配后的处理。注意,需要迭代处理一直到P[k+1]=T[i+1]或者k=1。

KMP 算法在搜索阶段,最坏和平均时间复杂度都是 O(n)。

4. 实现代码
实现代码分三部分:
yc_kmp_init:  生成前缀函数π
yc_kmp_find: 匹配过程
yc_strstr_find: 封装strstr一致接口。
对于多次搜索某确定模式的问题,一般会生成前缀函数一次并保存,然后直接调用yc_kmp_find实现,这样可以省去多次计算同一前缀函数的问题。

void YC_PUBLIC_CALLTYPE yc_kmp_init(const char *str_patn, size_t sz_patn, size_t *psz_table) { size_t i, j; assert ( str_patn ); psz_table[0] = 0; for ( i = 1, j = 0; i < sz_patn; ++i ) { while ( j && str_patn[j] != str_patn[i] ) { j = psz_table[j-1]; } if ( str_patn[j] == str_patn[i] ) { ++j; } psz_table[i] = j; } } char * YC_PUBLIC_CALLTYPE yc_kmp_find(const char *str_text, size_t sz_text, const char *str_patn, size_t sz_patn, const size_t *psz_table ) { size_t i, j; assert( str_text && str_patn && psz_table ); for ( i = 0, j = 0; i < sz_text; ++i ) { while ( j && str_text[i] != str_patn[j] ) { j = psz_table[j - 1]; } if ( str_text[i] == str_patn[j] ) { ++j; if ( sz_patn == j ) { return (char*)str_text + i + 1 - sz_patn; } } } return NULL; } char * YC_PUBLIC_CALLTYPE yc_strstr_kmp(const char *str_text, const char *str_patn) { char *str_rtn = NULL; size_t sz_patn, sz_text; assert( str_text && str_patn ); sz_patn = strlen(str_patn); sz_text = strlen(str_text); if ( sz_patn && sz_text ) { size_t *psz_table = (size_t*)malloc(sizeof(size_t)*sz_patn); if ( psz_table ) { yc_kmp_init(str_patn, sz_patn, psz_table); str_rtn = yc_kmp_find(str_text, sz_text, str_patn, sz_patn, psz_table); free(psz_table); } else { str_rtn = (char*)strstr(str_text, str_patn); } } return str_rtn; }

5. 结束语
感谢您阅读本文,您可以任意转载、更改、或者本文的任意部分用于任何用途(包括商业用途)。转载请注明出处。
欢迎指出BUG、缺陷及相关问题。十分感谢。

您也可以到我的下载资源下载PDF格式的文档。

模式匹配003: KMP相关推荐

  1. 字符串的模式匹配,KMP算法

    KMP算法是模式匹配的一种改进的算法,所谓的模式匹配也就是对于两个字符串主串S和模式串T.从主串的S的pos个字符起和模式串中的第一个字符进行比较,如果相等,则继续比较后面的字符,否则从主串的下一个字 ...

  2. 算法:模式匹配之KMP算法

    前言: 昨天看到<算法导论>里的第32章:字符串匹配,说到一个关于字符串匹配的很好的算法--KMP.关于KMP的内存含意以及KMP的来源,不是本文讲述的范畴,请感兴趣的读者自行查阅相关资料 ...

  3. 【模式匹配】KMP算法的来龙去脉

    1. 引言 字符串匹配是极为常见的一种模式匹配.简单地说,就是判断主串T中是否出现该模式串P,即P为T的子串.特别地,定义主串为T[0-n−1],模式串为P[0-p−1],则主串与模式串的长度各为n与 ...

  4. 串的模式匹配(KMP算法)

    [问题描述] 串的模式匹配算法实现(KMP算法) [输入形式] 第一行输入主串s: 第二行输入模式串t: 第三行输入起始位置pos: [输出形式] 输出模式串t的next值(以空格分隔) 输出模式匹配 ...

  5. 字符串的模式匹配(KMP)算法

    一.背景 给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题. Knuth-Morris-Pratt 算法(简称 KMP)是解决这一问题的常 ...

  6. C语言-模式匹配(KMP算法)

    什么是KMP算法? KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,简称KMP算法.KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与 ...

  7. 朴素模式匹配与KMP模式匹配算法

    一.朴素模式匹配 朴素模式匹配算法 就是遍历主串,然后把待匹配字符串与子串进行比对,先把待匹配子串的第一个字母与主串进行匹配,若匹配成功,则两串的坐标依次 ++,匹配不成功时,主串坐标返回到开始匹配时 ...

  8. 串的模式匹配、KMP算法、nextval数组求法

    一.暴力匹配 #include <iostream> using namespace std; #define MAXLEN 255 typedef struct{char ch[MAXL ...

  9. kmp有next和nextval的C语言,KMP模式匹配算法中next和nextval的求解(轉)

    KMP算法相關 轉載自:http://blog.sina.com.cn/s/blog_85b0ae450101j2iy.html KMP算法由兩部分組成: 第一部分,計算模式串的next或nextva ...

  10. a - 数据结构实验之串一:kmp简单应用_Java程序员必会之数据结构与算法全梳理

    常见的数据结构 链表 LinkedHashSet LinkedList 底层数据结构由链表和哈希表组成. 数据的添加和删除都较为方便,就是访问比较耗费时间. 数组 ArrayList 访问数据十分简单 ...

最新文章

  1. vc6中进行多行注释和反注释的方法
  2. SendDlgItemMessage
  3. 实战:向GitHub提交代码时触发Jenkins自动构建
  4. wxWidgets:wxSetCursorEvent类用法
  5. android自动回复退订,Android实现短信自动回复,挂电话
  6. uml 时序图_程序猿都应学习的语言:看 25 张图学 UML
  7. 【Flink】Flink Serving 天池快速上手 【视频笔记】
  8. 数据算法之希尔排序(shellSort)的Java实现
  9. servletContextListener定时任务使用
  10. linux中文件输出输入,linux中文件输入输出的管理
  11. u检验和t检验区别与联系
  12. 20180529-A · Comic book characters · ggplot2 geom_bar geom_text 柱状图 条形图 图例 · R 语言数据可视化 案例 源码
  13. 第三届中青杯数模本科组问题一———股票选择和投资组合方案(excel、python-Markowitz模型、夏普比率模型)
  14. [多点触控测试]不用app,手机怎么测试屏幕触控点数
  15. python常见的数据类型形式化定义_详解:规整数据(Tidy Data)的理论与Python实践
  16. 基于视觉导航机器人的快递分拣系统(开放源码附带论文和github仓库)
  17. codeup《算法笔记》题目索引(题目+答案+思路)
  18. Php 同步日历任务 .ics 文件
  19. org.apache.ibatis.builder.IncompleteElementException: Could not find result map com.zjj.dao.UserDao.
  20. 尾款人的双11焦虑症:配送机器人能解?

热门文章

  1. python适合做网站吗_python做网站有什么弊端文章伪原创的注意事项
  2. 原来多项式可以用来玩涂色游戏!
  3. Redis客户端大全
  4. 移动开发实验一:微信ui设计
  5. 微服务下基于RESTFUL风格实现统一的接口设计(JSON API)
  6. 机器学习--(一)机器学习基础
  7. es6与java的相似度_计算两个特征向量相似度的插件(es6.1.1-6.3+)
  8. python如何求一个列表里所有数字的和_Python求一个数字列表的元素总和
  9. word同时定义正文的中文字体和英文字体
  10. 剖析桌面化 Android操作系统的发展与未来