浅谈字符串匹配的KMP算法

来源:互联网 发布:什么是网络贷款诈骗 编辑:程序博客网 时间:2024/06/07 04:27

1 简介

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。(以上信息来自百度百科大笑)如果初次看不是太理解的话请无视,随着下面的讲解一切都会变得更加清晰。

2 传统字符串匹配算法

问题:假设已知字符串s和p,现在要在字符串s中查找字符串p
思路:首先从s的第一个字符开始往后与字符串p进行匹配,匹配失败后回溯到s第二个字符,再与字符串p进行匹配,失败后s再回溯到下一个字符位置,依次向下,直到匹配成功或走完字符串s;
int kmp(string &s,string &p){    int i=0,j=0;    if(s.length()<1||p.length()<1)    {        return -1;    }    while(i<s.length()&&j<p.length())    {        if(s[i]==p[j])        {            i++;            j++;                }        else         {            i=i-j+1;             j=0;        }    }    return i==p.length()?i-j:-1;}

3 kmp算法

1 求解next数组

首先我们要理解以下几个概念
最大相同缀:一个字符串长度最大的相同前缀后缀 (自己起的名字,由表述很容易理解)
不过还是举个例子吧 比如字符串ABDABCABD最大相同缀为ABD   然后我们:

把长度为n的字符串拆成n个字符串 下面我们来寻找这n个字符串的最大相同缀长度 我们用next[]存储 

例:字符串ABCDABD

  j           0  1   2   3   4   5   6 
              A  B  C  D   A   B  D
next       0   0  0   0   1    2  0

现在我们把next数组后移一位,首位变成-1,得到新的next数组
next   -1  0 0  0  0  1  2  
下面 详解next数组求解方式()
首先 next[0]=-1;
已知next[i]=j 求next[i+1]                   //next[5]=1;(由next[5]求next[6])
如果p[i]=p[j]  next[i+1]=next[i]+1=j+1;     //p[5]=p[1];next[6]=2;
                                            
                        //next[6]=2;p[6]!=p[2](由next[6]求next[7])//尽管这里next[7]并没有意义,无视吧
否则 令j=next[j]  因为可能存在更小的串      //j=next[2]=0;    
                                            
继续比较
int getnext(string &p){int i=0,j=-1;<pre name="code" class="cpp">        next[0]=-1;while(i<p.length()){if(j==-1||p[i]==p[j])//对于每个i这里得到i+1的next[]{i++;j++;next[i]=j;}else {j=next[j];}}}

现在找到了next数组,我们就可以开始kmp算法之旅了生气

2.利用next数组进行快速匹配

    假使现在要在字符串s:BBC ABCDAB ABCDABCDABDE中查找字符串p:ABCDABD只要进行以下步骤即可:
    初始化 i=j=0;

 1如果j == -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;

 2: 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。
换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,移动的实际位数为:j - next[j]此值大于等于1。

int kmp(string &s,string &p){getnext(p);//获取next数组值</span>int i=0,j=0;while(i<s.length()&&j<p.length()){if(j==-1||s[i]==p[j]){i++;j++;}else {j=next[j];}}return j==p.length()?i-j:-1;//返回起始位置}

对下面这一段没有兴趣的可以自行跳过
*************************************************************
下面我们把上面的例子按思路走一遍
s:BBC ABCDAB ABCDABCDABDE   
p: ABCDABD
失配,j=next[j]=-1;满足条件1;i++,j++;          (i=1,j=0
s:BBC ABCDAB ABCDABCDABDE   
p:  ABCDABD
再次失配,满足条件2;j=next[j]=-1;满足1;i++,j++;(i=2,j=0)

s:BBC ABCDAB ABCDABCDABDE   
p:   ABCDABD
再次失配,满足条件2;j=next[j]=-1;满足1;i++,j++;(i=3,j=0
s:BBC ABCDAB ABCDABCDABDE   
p:    ABCDABD
再次失配,满足条件2;j=next[j]=-1;满足1;i++,j++;(i=4,j=0

s:BBC ABCDAB ABCDABCDABDE   
p:     ABCDABD
匹配成功,满足1,i++;j++;                          (i=5;j=1)
s:BBC ABCDAB ABCDABCDABDE   
p:     ABCDABD
匹配成功,满足1,i++;j++;                       (i=6;j=2)
s:BBC ABCDAB ABCDABCDABDE   
p:     ABCDABD
......
匹配成功,满足1,i++;j++;                     (i=9;j=6)
s:BBC ABCDAB ABCDABCDABDE   
p:     ABCDABD
匹配失败,j=next[j]                               (i=9;j=2)
s:BBC ABCDAB ABCDABCDABDE   
p:         ABCDABD
匹配失败,j=next[j]                               (i=9;j=0)
s:BBC ABCDAB ABCDABCDABDE   
p:           ABCDABD

匹配失败,j=next[j]                               (i=9;j=-1)

满足1,i++;j++                                (i=10;j=0) 
s:BBC ABCDAB ABCDABCDABDE   
p:            ABCDABD
......
满足1,i++;j++                                 (i=16;j=6) 
s:BBC ABCDAB ABCDABCDABDE   
p:            ABCDABD
匹配失败,j=next[j]=2;                             (i=16;j=2)
s:BBC ABCDAB ABCDABCDABDE   
p:                ABCDABD
......
s:BBC ABCDAB ABCDABCDABDE   
p:                ABCDABD
匹配完成
*************************************************************


0 0
原创粉丝点击