KMP高质量代码实现详解
KMP算法
对于KMP算法我分为两个部分说明,第一部分是算法部分,介绍KMP算法的算法思想;第二部分是实现部分,介绍一种厉害的实现代码以及代码注释。当然了由于本文主要介绍怎么实现故而先分析实现,对KMP理解不是很透彻的朋友可以先读算法介绍部分再来看代码。
(2). 算法实现:
首先提一句,当然在理解KMP的时候也很关键啦,就是说”KMP algorithm never re-compares a character in T that has matched a character in P”.其中T是目标串文本,P是模式串
下面给出大牛代码(来源不详)。
#define MAX_N 10010 char T[MAX_N], P[MAX_N]; //n为length of T , m 为length of P
int next[MAX_N],n,m; //compute array next[], next[i]表示P[0…i-1]中的
//the length of the longest proper prefix that matches a proper suffix
void kmpPreprocess()
{
int i=,j=-;
next[]=-;
while(i<m)//①为什么这一段代码可以顺利计算出next[]数组的值???
15 {
16 while(j>=0 && P[i]!=P[j])j=next[j];
17 i++, j++;
18 next[i]=j;
19 }
} void kmpSearch()
{
int i=,j=;
while(i<n)
{
while(j>= && T[i]!=P[j])j=next[j];
i++, j++;
if(j==m)
{
printf(“P is found at index %d in T\n”,i-j);
j=next[j];
}
}
}
下面就来分析一下为什么上述标红代码可以顺利计算出next[]数组的值。
首先,我们明确两点
- next[i]表示P[0…i-1]中的the length of the longest proper prefix that matches a proper suffix
- 每次循环开始的时候总有j=next[i]成立,也就是说明j是P[0…i-1]中的the length of the longest proper prefix that matches a proper suffix
其次,我们说明一下怎么去计算next[i+1],这儿有两种情况。
- 当P[i]==p[j]时,那么next[i+1]=next[i]+1; 这个比较容易理解就不多说了。
- 如果不等,在这种情况下代码上面给出的操作是j=next[j],什么意思呢,也就是说将j赋值为P[0..j-1] 中的the length of the longest proper prefix that matches a proper suffix,然后再来比较P[i]与p[j]的关系,递归。但是,为什么这么做是正确的呢?
这里我们来详细阐述一下,我们先来转换一下问题。
首先我们明确一点,那就是P[i]!=p[j]情况下next[i+1]的值比 P[i]==p[j]时候next[i+1]的值要小。又因为P[0…j-1]==P[i-j…i-1],故而此时P[0…i]的the length of the longest proper prefix that matches a proper suffix等价于P[i-j…i] 的the length of the longest proper prefix that matches a proper suffix。于是我们要先找到P[i-j,i-1]中的the length of the longest proper prefix that matches a proper suffix,等于next[j]。
(1). 算法介绍:
这一部分我就不自己写了,因为我已经看到有人写的足够好的讲解了。
转自:http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/
The Partial Match Table
The key to KMP, of course, is the partial match table. The main obstacle between me and understanding KMP was the fact that I didn’t quite fully grasp what the values in the partial match table really meant. I will now try to explain them in the simplest words possible.
Here’s the partial match table for the pattern “abababca”:
|
1 2 3 |
char: | a | b | a | b | a | b | c | a | index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | |
If I have an eight-character pattern (let’s say “abababca” for the duration of this example), my partial match table will have eight cells. If I’m looking at the eighth and last cell in the table, I’m interested in the entire pattern (“abababca”). If I’m looking at the seventh cell in the table, I’m only interested in the first seven characters in the pattern (“abababc”); the eighth one (“a”) is irrelevant, and can go fall off a building or something. If I’m looking at the sixth cell of the in the table… you get the idea. Notice that I haven’t talked about what each cell means yet, but just what it’s referring to.
Now, in order to talk about the meaning, we need to know about proper prefixes and proper suffixes.
Proper prefix: All the characters in a string, with one or more cut off the end. “S”, “Sn”, “Sna”, and “Snap” are all the proper prefixes of “Snape”.
Proper suffix: All the characters in a string, with one or more cut off the beginning. “agrid”, “grid”, “rid”, “id”, and “d” are all proper suffixes of “Hagrid”.
With this in mind, I can now give the one-sentence meaning of the values in the partial match table:
The length of the longest proper prefix in the (sub)pattern that matches a proper suffix in the same (sub)pattern.
Let’s examine what I mean by that. Say we’re looking in the third cell. As you’ll remember from above, this means we’re only interested in the first three characters (“aba”). In “aba”, there are two proper prefixes (“a” and “ab”) and two proper suffixes (“a” and “ba”). The proper prefix “ab” does not match either of the two proper suffixes. However, the proper prefix “a” matches the proper suffix “a”. Thus, the length of the longest proper prefix that matches a proper suffix, in this case, is 1.
Let’s try it for cell four. Here, we’re interested in the first four characters (“abab”). We have three proper prefixes (“a”, “ab”, and “aba”) and three proper suffixes (“b”, “ab”, and “bab”). This time, “ab” is in both, and is two characters long, so cell four gets value 2.
Just because it’s an interesting example, let’s also try it for cell five, which concerns “ababa”. We have four proper prefixes (“a”, “ab”, “aba”, and “abab”) and four proper suffixes (“a”, “ba”, “aba”, and “baba”). Now, we have two matches: “a” and “aba” are both proper prefixes and proper suffixes. Since “aba” is longer than “a”, it wins, and cell five gets value 3.
Let’s skip ahead to cell seven (the second-to-last cell), which is concerned with the pattern “abababc”. Even without enumerating all the proper prefixes and suffixes, it should be obvious that there aren’t going to be any matches; all the suffixes will end with the letter “c”, and none of the prefixes will. Since there are no matches, cell seven gets 0.
Finally, let’s look at cell eight, which is concerned with the entire pattern (“abababca”). Since they both start and end with “a”, we know the value will be at least 1. However, that’s where it ends; at lengths two and up, all the suffixes contain a c, while only the last prefix (“abababc”) does. This seven-character prefix does not match the seven-character suffix (“bababca”), so cell eight gets 1.
How to use the Partial Match Table
We can use the values in the partial match table to skip ahead (rather than redoing unnecessary old comparisons) when we find partial matches. The formula works like this:
If a partial match of length partial_match_length is found and table[partial_match_length] > 1, we may skip ahead partial_match_length - table[partial_match_length - 1] characters.
Let’s say we’re matching the pattern “abababca” against the text “bacbababaabcbab”. Here’s our partial match table again for easy reference:
|
1 2 3 |
char: | a | b | a | b | a | b | c | a | index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | |
The first time we get a partial match is here:
|
1 2 3 |
bacbababaabcbab | abababca |
This is a partial_match_length of 1. The value at table[partial_match_length - 1] (or table[0]) is 0, so we don’t get to skip ahead any. The next partial match we get is here:
|
1 2 3 |
bacbababaabcbab ||||| abababca |
This is a partial_match_length of 5. The value at table[partial_match_length - 1] (or table[4]) is 3. That means we get to skip ahead partial_match_length - table[partial_match_length - 1] (or 5 - table[4] or 5 - 3 or 2) characters:
|
1 2 3 4 5 |
// x denotes a skip bacbababaabcbab xx||| abababca |
This is a partial_match_length of 3. The value at table[partial_match_length - 1] (or table[2]) is 1. That means we get to skip ahead partial_match_length - table[partial_match_length - 1] (or 3 - table[2] or 3 - 1 or 2) characters:
|
1 2 3 4 5 |
// x denotes a skip bacbababaabcbab xx| abababca |
At this point, our pattern is longer than the remaining characters in the text, so we know there’s no match.
Conclusion
So there you have it. Like I promised before, it’s no exhaustive explanation or formal proof of KMP; it’s a walk through my brain, with the parts I found confusing spelled out in extreme detail. If you have any questions or notice something I messed up, please leave a comment; maybe we’ll all learn something.
KMP高质量代码实现详解的更多相关文章
- 算术编码Arithmetic Coding-高质量代码实现详解
关于算术编码的具体讲解我不多细说,本文按照下述三个部分构成. 两个例子分别说明怎么用算数编码进行编码以及解码(来源:ARITHMETIC CODING FOR DATA COIUPRESSION): ...
- spark最新源码下载并导入到开发环境下助推高质量代码(Scala IDEA for Eclipse和IntelliJ IDEA皆适用)(以spark2.2.0源码包为例)(图文详解)
不多说,直接上干货! 前言 其实啊,无论你是初学者还是具备了有一定spark编程经验,都需要对spark源码足够重视起来. 本人,肺腑之己见,想要成为大数据的大牛和顶尖专家,多结合源码和操练编程. ...
- jdk1.8源码包下载并导入到开发环境下助推高质量代码(Eclipse、MyEclipse和Scala IDEA for Eclipse皆适用)(图文详解)
不多说,直接上干货! jdk1.8 源码, Linux的同学可以用的上. 由于源码JDK是前版本的超集, 所以1.4, 1.5, 1.6, 1.7都可以用的上. 其实大家安装的jdk路径下,这 ...
- 编写高质量代码改善C#程序的157个建议——导航开篇
前言 由于最近工作重心的转移,原来和几个同事一起开发的项目也已经上线了,而新项目就是在现有的项目基础上进行优化延伸扩展.打个比方,现在已经上线的项目行政案件的Web管理网站(代码还没那么多相比较即将要 ...
- 【iOS 使用github上传代码】详解
[iOS 使用github上传代码]详解 一.github创建新工程 二.直接添加文件 三.通过https 和 SSH 操作两种方式上传工程 3.1https 和 SSH 的区别: 3.1.1.前者可 ...
- 每周一书-编写高质量代码:改善C程序代码的125个建议
首先说明,本周活动有效时间为2016年8月28日到2016年9月4日.本周为大家送出的书是由机械工业出版社出版,马伟编著的<编写高质量代码:改善C程序代码的125个建议>. 编辑推荐 10 ...
- Scala 深入浅出实战经典 第64讲:Scala中隐式对象代码实战详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- Scala 深入浅出实战经典 第63讲:Scala中隐式类代码实战详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- 博友的 编写高质量代码 改善java程序的151个建议
编写高质量代码 改善java程序的151个建议 http://www.cnblogs.com/selene/category/876189.html
随机推荐
- Android之Activity的四种启动模式
当应用运行起来后就会开启一条线程,线程中会运行一个任务栈,当Activity实例创建后就会放入任务栈中.Activity启动模式的设置在AndroidManifest.xml文件中,通过配置Activ ...
- JavaScript高级程序设计之数值数组排序
如果数组中全是Nunber类型,则可以按照数值大小排序 , , , , ]; // asc升序函数 function compareAsc(value1, value2) { if (value1 & ...
- 2015年iOS测试现状
本文由 伯乐在线 - nathanw 翻译,dopcn 校稿.未经许可,禁止转载! 英文出处:www.mokacoding.com.欢迎加入翻译小组. 几周前,我决定将将我在 mokacoding 上 ...
- 【转载】ITU-RBT.656视频标准接口
ITU-RBT.656视频标准接口 ITU-RBT.656视频标准接口PAL制式(720*576)每场由四部分组成. ● 有效视频数据,分为奇场和偶场,均由288行组成.每行有1440个字节,其中72 ...
- JAVA素数分解
package test; import java.util.*; public class test1 { public static void main(String[] args){ long ...
- 推荐一款系统软件:Unity tweak tool
功能很多慢慢体会 在软件中心搜索unity tweak tool安装
- Xcode 7遇到 App Transport Security has blocked a cleartext HTTP 错误
今天用Xcode 7 创建新项目用到 URL 发送请求时,报下面的错: “App Transport Security has blocked a cleartext HTTP (http://) r ...
- NET Core中使用Redis
NET Core中使用Redis 注:本文提到的代码示例下载地址> https://code.msdn.microsoft.com/How-to-use-Redis-in-ASPNET-0d82 ...
- cocos2dx 3.x中的渲染机制
1.由2.x的渲染节点,变成添加渲染命令,可以避免重复渲染相同的节点,提高了渲染效率 2.单机游戏通常要求apk包在30M以内,没压缩1M会有1%的转换率(下载转换率),即收入会提高 3.2.x中首先 ...
- C中的一些函数
简述:printf.sprintf函数 转载自http://www.cnblogs.com/adslg/archive/2008/08/22/1274164.html 部分进行了修改,参考http:/ ...