这篇博文写的实在是太糟蹋了,请大家移步KMP算法小结,如果未能帮大家解惑,请见谅,并欢迎再新博文下面留言讨论。
KMP是比较知名的一个字符串匹配算法。由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现(不明白什么叫同时发现+_+)因此得名KMP算法。
首先大家想一下字符串如何匹配?
比如str1 = “BBC ABCDAB ABCDABCDABDE”,想知道这个字符串中是否包含str2 = “ABCDABD”。
逗逼:“这尼玛欺负我不会拼contains吗?”回答正确。但是contains的实现是怎样的?不用看源码,先自己想一下有木有思路。
一个简单直接的思路是:从str1第1个字母开始匹配,如果成功,good,匹配第2个...直到结束或者第i个字母不相等,然后从str2的第2个字母开始匹配....
是的,JDK源码就是这么做的。详见Implement strStr
这个方法是暴力算法,也可以说回溯。一般而言,暴力算法往往都不是最优的。KMP算法即是字符串匹配的优化算法。
本博文主要先讲一下KMP算法为什么比暴力算法更优。
在这之前,先给大家普及两个概念:前缀 & 后缀。
拿一个例子来讲:“china”
前缀::“c”,“ch”,“chi”,“chin”
后缀:"a","na",“ina”,"hina"
不知道大家有没有看明白,
所谓前缀就是str.substring(0,n) —— 其中n从1 ~ str.length() - 1(n == 0时,返回一个空串,不算前缀)
所谓后缀就是str.substring(m)——其中m从1 ~ str.length()-1
接下来的部分整理自网络日志,感谢原作者。
看下面两个字符串
【我们采取Implement strStr中的定义,将上面的字符串称为haystack,下面的称为needle】
B与A不匹配,haystack后移一位,得到:
还是不匹配,继续后移:直到:
good,匹配成功,然后匹配下面needle的第2个字符。
B和B,还是相同,一直匹配,直到:
按照我们之前的暴力做法,看到D匹配失败之后,就回溯到:
继而从B往下匹配,相当于haystack的指针往后回溯了。这肯定是对的,但是每次needle不能完全匹配时,都需要回溯,因此最坏的时间复杂度是n*m
接下来看下KMP是怎么做到不回溯的
我们继续回到回溯前的这一刻:
虽然匹配到D时,匹配失败了,但是我们知道了之前的ABCDAB是成功匹配的,暴力算法没有用到这些信息。KMP算法即设法利用这些信息,并不能让needle 和 haystack无需回溯。从而提升效率。
要做到这一点,需要用到Partial match table——部分匹配表
至于该表怎么求的,一会儿再讲
好,再回到刚才的状态
D(下标为 j )与空格(下标设为 i )匹配失败,前面ABCDAB是匹配的,匹配长度为length(本例是6),查表得B(要查失配字母的前一个字母,或者最后一个匹配的字母)对应的value为2,因此我们将needle前移length - 2 位【代码是将 j 的值减去length - 2(本例是4位)】即得到
moveStep = partial_match_length - table[partial_match_length - 1]
看到这里是不是有点明白了,移动之后 空格 之前的AB是匹配好的。
这里KMP算法耍了一个小聪明,首先,我们看到AB是match的字符串ABCDAB的一个前缀,但是也是一个后缀,而这个前缀和后缀是相等的,对于haystack字符串而言,空格之前的AB本来是匹配后缀的,然后,我们将前缀移动过来跟它匹配,有点 x = y && y = z 得到 x = z 的感觉。
解释:x(前缀) = y(后缀) && y = z(后缀与haystack匹配) => x = z(前缀也可以和haystack匹配)
从而无须将指针回溯,肯定有人会问,这样直接跳过会不会漏掉中间的情况,答案当然是否定的。
提示一下,我们当时选:前缀==后缀时,选的是最长的,比如aaaa,满足条件的最长前缀(后缀)是aaa。
言归真正,空格 与C 还是不匹配的,match的字符串(AB)长度length == 2,而C前的B在Partial match table中的value为0,因此我们需要将needle前移动length - 0(即2位)得到
这种情况比较简单,haystack后移:
重复上述步骤,发现:这下发了。。。一直匹配到D才适配,这次needle移多少位呢?且看D的前的B的value为2,match的length = 6.因此前移4位,得到
然后继续往下匹配。找到一个!如果想继续找下去,查看D的value值 == 0,match的length == 7,则移动7位。跟之前的一样。
Partial match table——部分匹配表
首先来看PMT中的value的意义:
还以刚才的字符串needle为例
A的value为0,表示以字符串A,相等的前缀和后缀中,最大长度为0,显然,单字符压根就没有前后缀
B的value为0,表示字符串AB,它只有一个前缀:A,一个后缀B,不存在相等的前缀,后缀,因此=0
来看第二个B,表示字符串ABCDAB,它有一个前缀AB,同时也有一个后缀AB,长度为2,因此value=2
这样讲应该很明白了吧,上文中还举了一个例子“aaaa”
它的PMT应该是这样的
a | a | a | a |
0 | 1 | 2 | 3 |
第一个a,肯定value为0
第二个a,表示字符串aa,有一个前缀a,一个后缀a,因此value = 1
第三个a,表示字符串aaa,有前缀a,aa。有后缀aa,a,因此相等的最长前缀后缀应该是aa,value=2
最简单的代码实现如下:
public static int[] getPartialMatchTable(String s) { if (s == null) { return null; } int length = s.length(); int[] value = new int[length]; value[0] = 0; for (int i = 1; i < length; i++) { String tem = s.substring(0, i + 1); int prefixEnd = i; int suffixBegin = 1; String prefix; String suffix; while (prefixEnd > 0) { prefix = tem.substring(0, prefixEnd);//取前缀 suffix = tem.substring(suffixBegin);//取后缀 if (prefix.equals(suffix)) { value[i] = prefixEnd; break; } else { prefixEnd--; suffixBegin++; } } } return value; }
有了PMT,KMP算法实现起来就会容易地多了
相关推荐
KMP算法的代码实现通常使用C++、Java、Python等编程语言,代码简洁且易于理解,是算法学习的经典案例。 总的来说,KMP算法以其高效性和广泛的应用性,成为字符串匹配领域不可或缺的一部分。通过理解和掌握KMP算法,...
总的来说,掌握KMP算法及其C++和Java实现对于提升你的编程技能和应对考研中的数据结构题目至关重要。通过深入学习和不断练习,你将能够更高效地处理字符串匹配问题,为未来的学术和职业发展打下坚实基础。
如果代码涉及字符串处理,那么它可能实现了KMP算法。 5. **动态规划**:动态规划是一种解决复杂问题的有效方法,通过将问题分解为更小的子问题来求解。例如,Fibonacci数列可以通过动态规划实现,避免重复计算。 6...
### Java 实现 KMP 算法详解 #### KMP 算法概述 KMP 算法是由 Donald Knuth、James H. Morris 和 Vaughan Pratt 共同发明的一种高效字符串匹配算法。它通过预处理模式串来提高匹配效率,避免了传统模式匹配算法在...
10. **字符串处理**:Java中的String类提供了丰富的操作方法,如模式匹配、替换、分割等,涉及到KMP算法、Boyer-Moore算法等字符串匹配策略。 在《各种算法java实现.docx》这个文档中,你可能会找到以上算法的详细...
KMP算法通常用C++、Java等编程语言实现,其主要逻辑包括构建next数组、主函数中的匹配过程。这里我们给出一个简单的Python实现示例: ```python def compute_next(pattern): next = [0] j = -1 for i in range(1...
"如何通过Java代码实现KMP算法" KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后...
标题 "我的LeetCode和剑指offer刷题源码——Java版.zip" 提示这是一个包含Java编程语言实现的解题代码库,主要针对两个知名的在线编程挑战平台:LeetCode和剑指Offer(也称为《程序员面试金典》)。LeetCode是一个...
本资源“经典常用算法 Java和C语言两种实现”聚焦于将这些算法用两种广泛使用的编程语言——Java和C语言进行实现,旨在帮助开发者理解和应用这些基础且重要的算法。 1. **排序算法**: - **冒泡排序**:简单的比较...
《数据结构与算法分析——Java语言描述》是一本深度探讨数据结构和算法的书籍,它以Java编程语言为载体,详细阐述了各种重要的数据结构及其相关的算法实现。这本书对于计算机科学的学习者,尤其是软件开发者来说,是...
除此之外,还有字符串匹配算法,如KMP算法和Boyer-Moore算法,用于在文本中快速查找特定模式。在数据结构方面,栈、队列、链表、树、图等都是算法实现的基础,如平衡二叉搜索树(AVL树、红黑树)在数据库索引和搜索...
《Java算法第6章.ppt》可能专门讨论了字符串处理的算法,如KMP算法、Rabin-Karp算法,这些都是在文本处理和搜索问题中常见的技术。 《Java算法第7章.ppt》和《Java算法第8章.ppt》可能涉及到组合数学和概率论在算法...
本资源“java各种经典算法大全 还包括C语言的实现”涵盖了两个广泛使用的编程语言——Java和C,它们在实现算法方面各有特点。下面,我们将深入探讨这些经典算法以及它们在两种语言中的实现方式。 1. **排序算法**:...
《数据结构与算法分析——Java语言描述.pdf》这本书将涵盖以上所有内容,并通过实例和练习帮助读者巩固理解。学习这些知识不仅可以提升编程技能,也能为面试和项目开发做好准备。记住,理解并熟练运用数据结构和算法...
1. **KMP算法**: KMP(Knuth-Morris-Pratt)算法是一种高效的字符串搜索算法,由唐纳德·克努斯、詹姆斯·莫里斯和维诺德·普拉特于1977年提出。它避免了对已知模式的重复比较,显著提高了效率。KMP算法的核心是...
这两个核心的jar包——stblib.jar和algs4.jar,包含了实现各种经典算法的类库,是理解并实践算法的重要工具。 stblib.jar库主要是为了解决算法四中涉及的稳定性和排序问题。稳定性在排序算法中是一个关键概念,它...
《多种编程语言的算法集合——C++与Java的探索》 在编程的世界里,算法是解决问题的核心工具,它们是程序员的智慧结晶,是程序设计的灵魂。这个名为"多种编程语言的算法集合。_C++_Java_下载.zip"的压缩包文件,...
三、编程语言实战——Java 作为标签之一,Java 是实现算法的重要工具。熟悉 Java 的基本语法、类库以及面向对象特性对于算法实现至关重要: 1. **Java 数据结构实现**:理解如何用 Java 实现上述的数据结构,例如用...
本资源“Java || c || c++经典算法”集合了三种编程语言——Java、C和C++的算法实现,旨在帮助编程爱好者深入理解和掌握算法设计与实现。 Java是一种广泛使用的面向对象的编程语言,其强类型、自动内存管理以及丰富...