kmp匹配算法

在讲解kmp算法之前,先讨论下一般的模式匹配算法

先看下代码

int index(string s,string t,int pos)
{
	
	for (int i=pos;i<s.length()&&i+t.length()<=s.length();i++){
		int flag=1;
		for (int j=0;j<t.length();j++){
			if (s[i+j]!=t[j]){
				flag=0;
				break;
			}
		}
		if (flag){
			return i+1;
		}
	}
	return 0;
}

返回的是b串第一次在a串的pos位置后出现的位置

可以显而意见的是复杂度为O(n*m)

在a串和b串为如下的形式的时候时

a:0000000001

b:001

你会发现b串在a串中进行匹配的时候,要到最后一次才会匹配成功,而前面的匹配很多都做了无用功,i指针根本就没必要回朔


kmp算法

kmp算法巧妙的解决了这个问题

将问题的复杂度下降到O(n+m)

那么它是怎么实现的呢?

先给大家看几个图示


当a串与b串进行比较的时候,可能在分别在i和j的位置的时候匹配失败了

这个时候如果是一般匹配方法就会i指针回朔到b1+1位置,而j则会回朔到b2位置

但是如果仔细观察下你会发现(b1,i-1)和(b2,j-1)是完全匹配的,而且你也可能会发现可能在b串中本身就存在(b2,s)完全等于(k,j-1)的情况,那么这个时候何不直接把j回朔到s+1的位置呢?

如下图所示



这样算法复杂度就可以得到很好的优化

但是如何实现以上过程呢

这里引进next数组

next[i]:=如果匹配失败了j指针应该滑向的位置

也可以理解成最大的一个值使得对于b串而言(0,next[i]-1)完全等于(j-next[i],j-1)

则可以定义如下

next[i]=-1,i==0

next[i]=k,,0<k<j,其中"p0p1p2p3...pk-1"="pj-kpj-k+1pj-k+2...pj-1"

next[i]=0,其余的情况,其实也就是b串0到j-1的位置上没有前后相互匹配的情况


那么只要求出next数组的值,就万事大吉了

下面给出代码

void getNext(char *p,int *next)
{
    int j,k;
    next[0]=-1;
    j=0;
    k=-1;
    while(j<strlen(p)-1)
    {
        if(k==-1||p[j]==p[k])    //匹配的情况下,p[j]==p[k]
        {
            j++;
            k++;
            next[j]=k;
        }
        else                   //p[j]!=p[k]
            k=next[k];
    }
}
int KMPMatch(char *s,char *p)    //如果不存在匹配项,则返回-1,否在返回开始的匹配位置
{                                //p匹配到s,s必须比p长
    int next[100];
    int i,j;
    i=0;
    j=0;
    getNext(p,next);
    while(i<strlen(s))
    {
        if(j==-1||s[i]==p[j])
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];       //消除了指针i的回溯
        }
        if(j==strlen(p))
            return i-strlen(p);
    }
    return -1;
}

写法二,思路都是一样的,只是代码习惯的不同。。

#include <bits/stdc++.h>
using namespace std;
#define maxn 105
int next[maxn];

void get_next(string b)
{
	next[0]=-1;
	int j=0;
	for (int i=1;i<b.length();i++){
		while(j>0&&b[i]!=b[j]) j=next[j];
		if (b[i]==b[j]) next[i]=j++;
	}
}

int kmp(string a,string b,int pos)
{
	memset(next,0,sizeof(next));
	get_next(b);
	
	int j=0;
	for (int i=pos;i<a.length();i++){
		while(j>0&&a[i]!=b[j]) j=next[j];
		if (a[i]==b[j]) j++;
		if (j==b.length()){
			return i-b.length()+1;
		}
	}
	return -1;
}

int main()
{
	string a,b;
	while(cin>>a>>b){
		printf("%d\n",kmp(a,b,0));
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值