浅谈字符串匹配的KMP算法

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
匹配完成
*************************************************************


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值