版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。

一、KMP算法定义

【百度百科】KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特莫里斯普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。

字符串模式匹配是对字符串的基本操作之一,广泛应用于生物信息学、信息检索、拼写检查、语言翻译、数据压缩、网络入侵检测等领域,如何简化其复杂性一直是算法研究中的经典问题。字符串的模式匹配实质上就是寻找模式串P是否在主串T 中,且其出现的位置。我们对字符串匹配的效率的要求越来越高, 应不断地改良模式匹配算法,减少其时间复杂度。

KMP算法是由D.E. Knuth、J.H.Morris和V.R. Pratt提出的,可在一个主文本字符串S内查找一个词W的出现位置。此算法通过运用对这个词在不匹配时本身就包含足够的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符。这个算法是由高德纳和沃恩·普拉特在1974年构思,同年詹姆斯·H·莫里斯也独立地设计出该算法,最终由三人于1977年联合发表。该算法减少了BF算法中i回溯所进行的无谓操作,极大地提高了字符串匹配算法的效率。

二、算法说明

1.简单举例

设主串(下文中我们称作T)为:a b a c a a b a c a b a c a b a a b b

模式串(下文中我们称作W)为:a b a c a b

用暴力算法匹配字符串过程中,我们会把T[0] 跟 W[0] 匹配,如果相同则匹配下一个字符,直到出现不相同的情况,此时我们会丢弃前面的匹配信息,然后把T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了匹配效率。

而在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。

比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配。右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀后缀,然后移动使它们重叠。

在第一次匹配过程中

T: a b a c a a b a c a b a c a b a a b b

W: a b a c a b

在T[5]与W[5]出现了不匹配,而T[0]~T[4]是匹配的,其中T[0]~T[4]就是上文中说的已经匹配的模式串子串,移动找出最长的相同的前缀和后缀并使他们重叠:

T: a b a c aa b a c a b a c a b a a b b

W: a b a c a b

然后在从上次匹配失败的地方进行匹配,这样就减少了匹配次数,增加了效率。

然而,如果每次都要计算最长的相同的前缀反而会浪费时间,所以对于模式串来说,我们会提前计算出每个匹配失败的位置应该移动的距离,花费的时间就成了常数时间。

2.再说明:

  • 假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置

    • 如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
    • 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。
      • 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值(next 数组的求解会在下文的3.3.3节中详细阐述),即移动的实际位数为:j - next[j],且此值大于等于1。

很快,你也会意识到next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。

此也意味着在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)。如果next [j] 等于0或-1,则跳到模式串的开头字符,若next [j] = k 且 k > 0,代表下次匹配跳到j 之前的某个字符,而不是跳到开头,且具体跳过了k 个字符。

三、 扩展1:BM算法

KMP的匹配是从模式串的开头开始匹配的,而1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了一种新的字符串匹配算法:Boyer-Moore算法,简称BM算法。该算法从模式串的尾部开始匹配,且拥有在最坏情况下O(N)的时间复杂度。在实践中,比KMP算法的实际效能高。

BM算法定义了两个规则:

  • 坏字符规则:当文本串中的某个字符跟模式串的某个字符不匹配时,我们称文本串中的这个失配字符为坏字符,此时模式串需要向右移动,移动的位数 = 坏字符在模式串中的位置 - 坏字符在模式串中最右出现的位置。此外,如果"坏字符"不包含在模式串之中,则最右出现位置为-1。
  • 好后缀规则:当字符失配时,后移位数 = 好后缀在模式串中的位置 - 好后缀在模式串上一次出现的位置,且如果好后缀在模式串中没有再次出现,则为-1。

四、扩展2:Sunday算法

上文中,我们已经介绍了KMP算法和BM算法,这两个算法在最坏情况下均具有线性的查找时间。但实际上,KMP算法并不比最简单的c库函数strstr()快多少,而BM算法虽然通常比KMP算法快,但BM算法也还不是现有字符串查找算法中最快的算法,本文最后再介绍一种比BM算法更快的查找算法即Sunday算法。

Sunday算法由Daniel M.Sunday在1990年提出,它的思想跟BM算法很相似:

  • 只不过Sunday算法是从前往后匹配,在匹配失败时关注的是文本串中参加匹配的最末位字符的下一位字符。

    • 如果该字符没有在模式串中出现则直接跳过,即移动位数 = 匹配串长度 + 1;
    • 否则,其移动位数 = 模式串中最右端的该字符到末尾的距离+1。

更详细的介绍请参考很详尽KMP算法(厉害)

五、为什么JDK String为什么不使用KMP算法?

在JDK1.8中我点开了String的indexOf(String str)发现并没有使用KMP算法。那么为什么JDK不使用KMP算法呢?

1)大部分比较是短字符串,普通算法的O(nm)已经够用,而KMP算法在较短字符串里是O(n+m)。KMP算法的常数因子会拖慢算法。

2) 因为是公共库函数,需要考虑各种情况的性能。可能你也不想突然的内存开销。

3)也许未来的JDK版本会像Hashmap里的红黑树一样增加特定情况的算法优化。

六、KMP核心代码

    private static int[] getNext(String pattern){

        int j = 0,k = -1;
int[] next = new int[pattern.length()];
next[0] = -1;
while(j < pattern.length() - 1){
if(k == -1 || pattern.charAt(j) == pattern.charAt(k)){
j++;
k++;
//改进next数组
if(pattern.charAt(j) != pattern.charAt(k)){
next[j] = k;
}else{
next[j] = next[k];
}
}else{
k = next[k];
}
}
return next;
} public static int indexOf(String target, String pattern){ int i = 0,j = 0; int[] next = getNext(pattern); while(i < target.length()){ if(j == -1 || target.charAt(i) == pattern.charAt(j)){ i++;
j++;
}else{
j = next[j];
}
if(j == pattern.length()){ return i - j;
}
}
return -1;
}
 

七、一点小总结

  1. KMP算法在匹配过程中将维护一些信息来帮助跳过不必要的检测,这个信息就是KMP算法的重点 --next数组。(也叫fail数组,前缀数组)。所以,这个next数组很关键。
  2. 有了next数组,我们就可以通过next数组跳过不必要的检测,加快字符串匹配的速度。
  3. KMP算法的精髓在于对已知信息的充分利用,这体现在待匹配串的匹配上面,更用于预处理时自己匹配自己上面。
  4. 每一趟匹配完成后,利用上一趟匹配的结果,将模式向右滑动尽可能远的一段距离。

我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!

参考资料:

  1. https://www.cnblogs.com/ZuoAndFutureGirl/p/9028287.html
  2. https://baike.baidu.com/item/kmp%E7%AE%97%E6%B3%95/10951804?fr=aladdin
  3. https://www.jianshu.com/p/ef5501c2c968
  4. https://blog.csdn.net/leejuen/article/details/88689399
  5. https://www.jianshu.com/p/0267b76368d1
  6. https://blog.sengxian.com/algorithms/kmp

程序员的算法课(11)-KMP算法的更多相关文章

  1. 【转】成为Java顶尖程序员 ,看这11本书就够了

    成为Java顶尖程序员 ,看这11本书就够了 转自:http://developer.51cto.com/art/201512/503095.htm 以下是我推荐给Java开发者们的一些值得一看的好书 ...

  2. 算法笔记之KMP算法

    本文是<算法笔记>KMP算法章节的阅读笔记,文中主要内容来源于<算法笔记>.本文主要介绍了next数组.KMP算法及其应用以及对KMP算法的优化. KMP算法主要用于解决字符串 ...

  3. 算法起步之kmp算法

    [作者Idlear  博客:http://blog.csdn.net/idlear/article/details/19555905]            这估计是算法连载文章的最后几篇了,马上就要 ...

  4. 程序员代码面试指南 IT名企算法与数据结构题目最优解

    原文链接 这是一本程序员面试宝典!书中对IT名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现.针对当前程序员面试缺乏权威题目汇总这一痛点,本书选取将近200道真实出现过的经典代码面试题,帮 ...

  5. 成为Java顶尖程序员 ,看这11本书就够了(转)

    学习的最好途径就是看书",这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一本好书的 ...

  6. 成为Java顶尖程序员 ,看这11本书就够了

    以下是我推荐给Java开发者们的一些值得一看的好书.但是这些书里面并没有Java基础.Java教程之类的书,不是我不推荐,而是离我自己学习 Java基础技术也过去好几年了,我学习的时候看的什么也忘了, ...

  7. 图灵社区 书单推荐:成为Java顶尖程序员 ,看这11本书就够了

    java书单推荐 转自 http://www.ituring.com.cn/article/211418 “学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两 ...

  8. [转]成为Java顶尖程序员 ,看这11本书就够了

    “学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一本好书的价值远超 ...

  9. 程序员的复仇:11行代码如何让Node.js社区鸡飞狗跳

    来源自:http://www.techug.com/node-js-community 几天前,一名 NPM(Node.js Package Manager)社区的贡献者 Azer Koçulu 出于 ...

  10. Web程序员最常用的11款PHP框架

    PHP框架是Web程序员和开发人员最为有用的工具. PHP框架可以帮助用户更快地开发项目. 今天我将为开发人员带来几款最好的PHP框架,希望能对你有用. 1.Agavi Agavi是一款强大的,可扩展 ...

随机推荐

  1. Linux之ELF文件初探

    对比windowsPE文件与概述 在windows中可执行文件是pe文件格式,Linux中可执行文件是ELF文件,其文件格式是ELF文件格式,在Linux下的ELF文件除了可执行文件(Excutabl ...

  2. Centos 7修改hostname浅析

    之前写过一篇博客"深入理解Linux修改hostname",里面总结了RHEL 5.7下面如何修改hostname,当然这篇博客的内容其实也适用于CentOS 6,但是自CentO ...

  3. MySQL的统计信息学习总结

    统计信息概念 MySQL统计信息是指数据库通过采样.统计出来的表.索引的相关信息,例如,表的记录数.聚集索引page个数.字段的Cardinality.....MySQL在生成执行计划时,需要根据索引 ...

  4. NOIP模拟 16

    嗯我已经是个不折不扣的大辣鸡了 上次的T3就弃了,这次又弃 颓废到天际 T1 巨贪贪心算法 我就是一个只会背板子的大辣鸡 全裸的贪心看不出来,只会打板子 打板子,加特判,然后一无进展,原题不会,这就是 ...

  5. csps模拟测试70

    又炸了,T1没开$long long$,炸掉$50pts$,昨天因为SB错误挂掉$100pts$. 我kuku了,以后细心点吧.

  6. windows下安装nginx和基本配置

    1.下载并安装nginx 到nginx官网上下载相应的安装包,http://nginx.org/en/download.html: 下载之后进行解压,将解压后的文件放到自己心仪的目录下,如下图所示: ...

  7. Java基础系列5:Java代码的执行顺序

    该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 一.构造方法 构造方 ...

  8. php imagick设置图片圆角的方法

    php imagick设置图片圆角的方法 <pre>header('Content-Type: image/png'); $image = new Imagick('http://stat ...

  9. win10 visual studio 2017环境中安装CUDA8

    从https://developer.nvidia.com/cuda-toolkit-archive下载CUDA 8 安装 从https://developer.nvidia.com/gamework ...

  10. spark-Worker内部工作流程