【原创】通俗易懂的讲解KMP算法及代码实现

一、本文简介

本文的目的是简单明了的讲解KMP算法的思想及实现过程。

网上的文章的确有些杂乱,有的过浅,有的太深,希望本文对初学者是非常友好的。

其实KMP算法有一些改良版,这些是在理解KMP核心思想后的优化。

所以本文重点是讲解KMP算法的核心,文章最后会有涉及一些改良过程。

二、KMP算法简介

KMP算法是字符串匹配算法的一种。它以三个发明者命名,Knuth-Morris-Pratt,起头的那个K就是著名科学家Donald Knuth。

三、KMP算法行走过程

首先我们先定义两个字符串作为示例,被匹配串 S = "abcabdabcabeab",匹配串 T = "abcabeab"。

我们的目标就是确定S中是否包含T。

KMP算法的核心是分析匹配串 T 的特征,看看匹配串 T 能告诉我们什么信息。我把 T 着色,如下

T = "abcabeab"

现在看起来似乎比较明显了,三个着色点都是重复的 "ab",似乎这个 T 能告诉我们它有重复的子串"ab"可以利用。那么它们到底怎么用?先不讲具体怎么用,先走一遍KMP这个过程,但是大家需要留意这个"ab"。

1.  

我们发现T在匹配成功 "abcab" 后到"e"时和S子串的"d"匹配不成功了。

这个时候我们可以得到的先验就是目前T匹配过的S子串,就是"abcab"。而T本身就是"ab"重复的。所以"abcab"可以直接跳到第二个重复"ab"的位置,因为"abcab"中其它字符串开头不可能产生和T对应的匹配,这是很直观的。

因此T应该直接后移三个位置,并且用第三位的"c"和S刚才不匹配的"d"进行比较。

可以发现,S匹配的过程是不会回退的。因此匹配过程是S从头到尾的一遍扫描(中间可能因为匹配成功退出),所以这个查找过程是O(N)的复杂度。

2.

此时的匹配串是 "ab", 它告诉我们目前匹配的是"ab"。这个不像"abcab"出现了重复"ab",所以我们知道包括S[5]的"d"之前的子串是垃圾串。因此跳过S[5]重新开始匹配T。

最后完成匹配,可见,这样一次对S的扫描完成了对T的查找。

那么对于机器如何实现?第四节会分析。

四、KMP算法核心解析

对于上面的过程,我们抽离出来的话,问题的根本就是对T串重复情况的一个判定。不管S是什么串,只要对T的构造模式分析清楚就可以完成上述跳转过程。

因此需要一个数组记录这个T的 模式函数。

这里先给出这个T的模式函数

1.每个字母对应模式函数的值就是匹配到当前位置 i 后,下一次T开始进行比较的下标。

2.而S的移动长度为 i - F(i-1)。

对应上面两个问题

1.比如上面的匹配到的 "abcab" ,是T匹配到 T[5] 即"e"的时候出错的。那么我们需要查看上一个字符的模式函数值,因为上一个函数值才代表了已经匹配的串。

发现F(4)=2,说明下次比较从T[2]即"c"开始。因为"abcab"有重复"ab",第一个"ab"不需要比较。

2.而S下标移动多少呢?S下标的移动即找到T的初试位置对应的S的下标。对应上面第二张图,S[3]和T的移动对应起来了。3的获得就是通过上面公式 5-F(4)得到的。其实这个结果和1得到匹配位置的思路是一样的,不过又向前移动对齐了开头。

为什么如此构造这个模式函数?

就是因为F值表示了对于位置 i, T[i]有无重复,并且重复下标的位置在哪(F[i])。既然我们获取了重复下标的位置,那么其它的相关值可以推出来了。

基于这个思路,再给出另外两个T串的模式函数值,帮助大家思考。

1.

2.

如何快速构造这个模式函数?

这个留给大家思考一下了,应该也比较直接了,注意是查找重复位置。如果不大明白,可以参考下面的代码。

五、KMP算法实现

 1 /*
 2     return val means the begin pos of haystack
 3     -1 means no matching substring
 4 */
 5 int KMP(char *haystack, char *needle) {
 6     // pre-process
 7     if(haystack[0] == 0 && needle[0] == 0)
 8         return 0;
 9
10     int i, j, k, min, cur;
11
12     //construct F(t) in vector len
13     vector<int > len;
14     len.push_back(0);
15     for(i=1; needle[i] != 0; i++){
16         if(len[i-1] == 0){
17             if(needle[i] == needle[0])
18                 len.push_back(1);
19             else
20                 len.push_back(0);
21         }else{
22             if(needle[i] == needle[len[i-1]])
23                 len.push_back(len[i-1]+1);
24             else
25                 len.push_back(0);
26         }
27     }
28     // KMP finder
29     j = 0;
30     for(i=0; haystack[i] != 0; ) {
31         // matching
32         for(; needle[j] != 0; j++) {
33             if(haystack[i+j] != needle[j])
34                 break;
35         }
36         //finded
37         if(needle[j] == 0)
38             return i;
39         else{ // jump
40             if(j){
41                 cur = j - len[j-1];
42                 i += cur;
43                 j = len[j-1];
44             }else{
45                 j = 0;
46                 i++;
47             }
48         }
49     }
50     //match failed
51     return -1;
52 }

六、KMP算法改进

KMP算法有一些改进版本加速查找,一般可以通过S串中的一些信息加速匹配过程。

比如若  S = "aaaaaaaafaaaaaaaaaaaab", T = "aaaaaaab"。

在查找过程中,S中间的 "f" 起到了阻挡作用。但是由于我们只是考虑T的先验信息,遇到"f" 不匹配会导致T每次后移一步进行新的匹配,知道T的开头碰到了"f"。

但是如果我们加入"f"这个原串S的信息,由于"f" != "b" && "f" != "a" && i-1 = F(i-1) ,所以直接跳到"f"后进行新的匹配会更快速的查找。

但是这些改进都是基于KMP基础算法之上的,因此把握核心要点不仅省时省力,更能有效扩展。

七、参考

[1] 《字符串匹配的KMP算法》 http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

[2] 算法导论

转载请注明出处~   http://www.cnblogs.com/xiaoboCSer/p/4236668.html

时间: 2024-10-11 01:04:36

【原创】通俗易懂的讲解KMP算法及代码实现的相关文章

kmp算法c++代码实现

1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 #define max 5000 5 6 int t[max];//目标串 7 int p[max];//模式串 8 int next[max];//前缀函数 9 int n,m;//n为目标串的数目,m为模式串的数目 10 void function_prefix(int *s,int *next) 11 { 12 next[1]=next[0]=0;

KMP算法的代码实现

上周算法班的BEN老师花了1个小时讲自动机和KMP的关系,结果failed...明天又要上课了,花了半天时间看了下KMP,暂且停留在利用next求模式中的跳跃长度,自动机那个还不能理解... 具体的可以百度阮一峰的KMP算法. 看着什么前缀后缀,突然想到上下文无关文法乔姆斯基范式了....又想到了NFA和正则表达式的转换,是时候复习复习了.. 太晚了,直接上代码,明天继续看ML和统计学!加油! 1 #include <iostream> 2 #include <string> 3

字符串匹配KMP算法C++代码实现

看到了一篇关于<字符串匹配的KMP算法>(见下文)的介绍,地址:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html,这篇博客对KMP算法的解释很清晰,但缺点是没有代码的实现.所以本人根据这位大神的思路写了一下算法的C++实现. C++代码如下: #include <iostream> #include<string.h> using namesp

字符串匹配(KMP 算法 含代码)

主要是针对字符串的匹配算法进行解说 有关字符串的基本知识 传统的串匹配法 模式匹配的一种改进算法KMP算法 网上一比較易懂的解说 小样例 1计算next 2计算nextval 代码 有关字符串的基本知识 串(string或字符串)是由零个或多个字符组成的有限序列,一般记为 当中s是串的名,用单引號括起来的字符序列是串的值:ai(1<=i<=n)能够是字母.数值或其它字符.串中字符的数组 n称为串的长度.零个字符的串称为空串,它的长度为0 串中随意个连续的字符组成的子序列称为该串的子串. 包括子

Kmp算法Java代码实现

public class KMP { public static void main(String[] args) { String str="ababxbababcadfdsss"; String subStr="abcad"; int[] next=getNext(subStr.toCharArray()); int p=kmp(next,str.toCharArray(),subStr.toCharArray()); System.out.println(p)

KMP算法详解

这几天学习kmp算法,解决字符串的匹配问题,开始的时候都是用到BF算法,(BF(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果.BF算法是一种蛮力算法.)虽然也能解决一些问题,但是这是常规思路,在内存大,数据量小,时间长的情况下,还能解决一些问题,但是如果遇到一些限制时间和内存的字符串问

(转载)KMP算法详解(写的很好)

原文地址:http://www.cnblogs.com/yjiyjige/p/3263858.html KMP算法应该是每一本<数据结构>书都会讲的,算是知名度最高的算法之一了,但很可惜,我大二那年压根就没看懂过~~~ 之后也在很多地方也都经常看到讲解KMP算法的文章,看久了好像也知道是怎么一回事,但总感觉有些地方自己还是没有完全懂明白.这两天花了点时间总结一下,有点小体会,我希望可以通过我自己的语言来把这个算法的一些细节梳理清楚,也算是考验一下自己有真正理解这个算法. 什么是KMP算法: K

KMP算法的Next数组详解(转)

转载请注明来源,并包含相关链接. 网上有很多讲解KMP算法的博客,我就不浪费时间再写一份了.直接推荐一个当初我入门时看的博客吧: http://www.cnblogs.com/yjiyjige/p/3263858.html 这位同学用详细的图文模式讲解了KMP算法,非常适合入门. ---------------------------------------------------------------------------------------------- KMP的next数组求法是很

KMP算法&amp;next数组总结

http://www.cnblogs.com/yjiyjige/p/3263858.html KMP算法应该是每一本<数据结构>书都会讲的,算是知名度最高的算法之一了,但很可惜,我大二那年压根就没看懂过~~~ 之后也在很多地方也都经常看到讲解KMP算法的文章,看久了好像也知道是怎么一回事,但总感觉有些地方自己还是没有完全懂明白.这两天花了点时间总结一下,有点小体会,我希望可以通过我自己的语言来把这个算法的一些细节梳理清楚,也算是考验一下自己有真正理解这个算法. 什么是KMP算法: KMP是三位