`
yanfaguanli
  • 浏览: 662232 次
文章分类
社区版块
存档分类
最新评论

字符串匹配KMP算法详解

 
阅读更多


这篇文章主要是解释KMP算法的原理,KMP算法是BF(Brute Force)算法的一种改进算法,什么是BF算法这里不多做解释。

1.KMP算法实现思路:

  每当一趟匹配过程中出现字符比较不等时,不需要回溯主串上面的指针i而是利用已经计算出的模式串P在j位置前面的子串P0...Pj-1部分匹配值k将模式向右滑j-k个字符,然后继续进行比较。

2.什么是部分匹配值:

  首先这里要引入"前缀"和"后缀"的概念,

  (1)前缀:指除了最后一个字符以外,一个字符串的全部头部组合;

  (2)后缀:指除了第一个字符以外,一个字符串的全部尾部组合;

  部分匹配值:就是"前缀"和"后缀"的最长的共有元素的长度,如以字符串"ABCDABD"为例:

  - "A"的前缀和后缀都为空集,共有元素的长度为0;

  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

3.下面来看证明过程:
假设主串S的长度为n,模式串P的长度为m,i为主串S当前位置的指针,j为模式串P当前位置的指针:
  S0.....Si-jSi-j+1Si-j+2.......Si-2Si-1...........Sn
     P0P1P2...............Pj-2Pj-1 

即:Si-jSi-j+1Si-j+2...Si-1=P0P1P2...Pj-2Pj-1 (1-1)

当Si!=Pji不动,模式串P向右移动多少个字符最正确(即要保证不会漏掉可能的匹配或不会重复不必要的匹配过程)

如果P本身的每一个字符都不相同,那么就可以直接将模式串P向右移动j个字符,道理很简单因为P0!=P1!=P2...!=Pj-1,由上面等式(1-1)可知P0也不等于Si-jSi-j+1Si-j+2.......Si-2Si-1中的任何一个,所以可以直接从P0开始和Si进行下一轮比较(指针i不需要回溯,指针j回溯到模式串的起始位置)。

但是如果模式串P存在很多重复的字符如:abcabcabd这种情况时就不能直接将j指针移动到P0了,例如主串为fffffabcabcabcabcabdfffff时

              i       

       fffffabcabcabcabcabdfffff

         abcabcabd

              j

             ↑ 发现 c != d 即 Si!= Pj

此时应该怎么移动呢?如果直接将j移动到P0然后和Si比较则会出现漏掉匹配的情况即匹配结束后找不到匹配串,正确的做法是将j—>P5位置(相当于向右滑动3个位置)然后和Si继续比较,如下所示:

              i       

       fffffabcabcabcabcabdfffff

         abcabcabd

              j

为什么是移动到P5呢?这个P5是怎么来的?这个就是整个算法的关键点,理解了这一点也就理解了KMP算法的本质。

其实这个5就是Pj-1的部分匹配值k,移动字符个数=j-k=8-5=3(j=8,k=5)

根据上面字符串部分匹配值的定义可知当j=8时P0P1...Pj-1等于字符串abcabcab,该字符串的前缀和后缀的最长共有元素的长度为5,即abcabca和bcabcab重叠的部分最大长度为5。

那么这是什么原理呢?为什么P0P1...Pj-1的部分匹配值就是模式P在位置j失配时重新开始匹配的位置呢?为什么不需要回溯i指针及完全回溯j指针到P0,却不会出现漏掉匹配或者怎么能确保这种情况下是没有进行不必要的重复匹配呢?

下面去看分析:

当在j位置失配时有 Pj!= Si且等式 Si-jSi-j+1Si-j+2...Si-1=P0P1P2...Pj-2Pj-1必定成立

又由字符串部分匹配值的定义可知P0P1...Pk-1=Pj-kPj-k+1...Pj-1,上面的列子中即P0P1P2P3P4=P3P4P5P6P7(j=8,k=5)

因为:Pj-kPj-k+1...Pj-1=Si-kSi-k+1...Si-1,所以P0P1...Pk-1=Si-kSi-k+1...Si-1

前缀和后缀的最长共有元素的意思就是说当y>=k时不可能存在Pj-yPj-y+1...Pj-1=P0P1P2...Pj-y-1(这里是关键,y就是该字符串的某一个前缀和后缀的长度,k是该字符串的部分匹配值,所以不可能存在一个y>=k使得等式成立),只有当y<k时等式才会成立;因此可以推断出:

P0P1P2...Pj-y-1和Si-j+1Si-j+2Si-j+3...Si-1进行匹配时前面j-k次都不会匹配成功,这就是KMP算法中当失配时直接将模式串P向右滑动k个字符的原理。

模式串P的部分匹配值表怎么求,下篇博文里面再详细说明,其实关键点还是前缀和后缀以及部分匹配值的问题,把这个搞懂了就都懂了。

4.实现代码:

 1 public static int kmp(String source,String p){
 2         int[] next = getNext(p);
 3         int i=0,j=0;
 4         while(i<source.length()&&j<p.length()){
 5             if(source.charAt(i)==p.charAt(j)){
 6                 i++;
 7                 j++;
 8             }else if(j==0){
 9                 i++;
10             }else{
11                 j = next[j-1];    
12             }
13         }
14         if(j>=p.length())
15             return i-j;
16         return -1;
17     }
18     
19     /**
20      * Acquire pattern string p's partial match table
21      */
22     public static int[] getNext(String p){
23         int[] next = new int[p.length()];
24         int i=1,j=0;
25         next[0] = 0;
26         while(i<p.length()-1){
27             while(j>0&&p.charAt(i)!=p.charAt(j))
28                 j = next[j-1];
29             if(p.charAt(i)==p.charAt(j))
30                 j++;
31             next[i++] = j;
32         }
33         return next;
34     }
分享到:
评论

相关推荐

    KMP算法:高效字符串匹配算法详解

    KMP算法(Knuth-Morris-Pratt算法)是一种高效的字符串匹配算法,由Donald Knuth、Vaughan Pratt和James H. Morris共同发明。KMP算法通过使用一个称为“部分匹配表”或“next数组”的数组来减少字符串匹配过程中的...

    2.KMP算法:高效字符串匹配算法详解

    KMP算法(Knuth-Morris-Pratt算法)是一种高效的字符串匹配算法,由Donald Knuth、Vaughan Pratt和James H. Morris共同发明。KMP算法通过使用一个称为“部分匹配表”或“next数组”的数组来减少字符串匹配过程中的...

    字符串模式匹配KMP算法详解.doc

    我以前一直理解不上去KMP算法(说心里话,我有点笨),当我看到这篇文章时,我理解了,这篇文章不错,说得挺细的,而且还免费,下了看看

    KMP 字符串模式匹配详解

    KMP 字符串模式匹配详解 KMP算法是对传统模式匹配算法的较大改进,在传统的模式匹配算法中,当出现主串中的字符与子串中的字符不等时,同时向前回溯了两个指针,一个是主串的指针,一个是子串的指针。而KMP算法的...

    KMP/BM字符串匹配算法源码

    BF/KMP/BM 字符串单模匹配算法, 用途广泛; BF/KMP/BM 字符串单模匹配算法, 用途广泛; BF/KMP/BM 字符串单模匹配算法, 用途广泛;

    模式匹配KMP算法

    《数据结构》串章节字符串的模式匹配KMP算法详解

    KMP字符串模式匹配详解

    KMP字符串模式匹配详解 KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法

    kmp算法详解

    KMP字符串模式匹配详解,KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法

    KMP字符串模式匹配详解及程序

    这是数据结构中的经典算法——KMP字符串模式匹配的详解,并且有相关的程序,保证受益匪浅。

    kmp算法概述、原理及应用详解.pdf

    KMP算法是一种高效的字符串匹配算法,由D.E.Knuth、J.H.Morris和V.R.Pratt共同提出,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。这种算法主要用于解决字符串匹配问题,即从主字符串(text)中搜索一...

    严蔚敏 数据结构 kmp算法详解.pdf

    在匹配过程中,算法将模式串和文本串逐个字符进行比较。如果字符匹配,则继续比较下一个字符。如果字符不匹配,则算法使用失配表跳转到模式串中适当的位置,然后继续比较。 失配表的构造: 失配表 F[i] 表示模式串...

    c/c++程序之_KMP字符串模式匹配详解

    KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法。简单匹配算法的时间复杂度为O(m*n);KMP匹配算法。可以证明它的时间复杂度为O(m+n).。先来看一个简单匹配算法的函数:此算法的思想是...

    字符串的模式匹配详解--BF算法与KMP算法

    记录一下串里面的模式匹配,模式匹配,顾名思义就是给定一个被匹配的字符串,然后用一个字符串模式(模型)去匹配上面说的字符串,看后者是否在前者里面出现。常用的有2种算法可以实现,下面我们来具体探讨下

    KMP字符串模式匹配详解.doc

    KMP字符串模式匹配详解.doc,希望对在学数据结构与算法或对之感兴趣的人有所帮助!

    字符串的模式匹配详解–BF算法与KMP算法

     BF算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个字符和P的第二个字符;若不相等,则比较S的第二个字符和P的第一个字符,依次比较...

    kmp详解 kmp详解

    KMP算法是拿来处理字符串匹配的。换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串)。比如,字符串A="I'm matrix67",字符串B="matrix",我们就说B是A的子串。你可以委婉地问你的MM:...

    深入串的模式匹配算法(普通算法和KMP算法)的详解

    模式匹配最朴素的算法是回溯法,即模式串跟主串一个字符一个字符的匹配,当模式串中跟主串不匹配时,主串回溯到与模式串匹配开始的下一个位置,模式串回溯到第一个位置,继续匹配。算法的时间复杂度为O(m*n),算法...

    详解KMP算法以及python如何实现

    Knuth-Morris-Pratt(KMP)算法是解决字符串匹配问题的经典算法,下面通过一个例子来演示一下: 给定字符串”BBC ABCDAB ABCDABCDABDE”,检查里面是否包含另一个字符串”ABCDABD”。 1.从头开始依次匹配字符,如果...

Global site tag (gtag.js) - Google Analytics