[学习笔记] kmp

不知道开头写什么话

康康下面这个问题:

给定一个模式串和一个文本串,求模式串在文本串中出现的位置,次数.

一个显然的做法是对于文本串中的每个位置,都把模式串从头开始匹配.

但是这样的时间复杂度太高了,

所以我们有了接下来要讲的:kmp算法.

step 0

先定义一些东西.

(a):模式串.

(b):文本串.

(la):(a)的长度.

(lb):(b)的长度.

(next[i]):模式串中满足(a[1sim j]=a[i-j+1sim i])的最大的(j).

(f[i]):满足(a[1sim j]=b[i-j+1sim i])的最大的(j).

step 1

首先暴力的算法因为时间复杂度太高((O(la*lb)))而被抛弃...

所以我们要想想怎么排除掉一些多余的情况.

先举个例子:

(a):bababb.

(b):bababababababababb.

我们在第一次匹配的时候会是这样:

babab|b

babab|ababababababb

在第六位的时候失配了..

但是我们可以发现,前五位的(f[i])都已经求出来了.

如果我们采取暴力的做法,将(a)直接往后移一位时,就是这样:

|bababb

b|ababababababababb

可以发现在第一位就失配了,

所以对第六位没有任何帮助.

于是我们可以想想,

要怎样跳才能一步跳到有用的位置.

这时我们就需要这个next数组.

step 2

(首先next数组的定义我已经放上面了...)

假如我们已经求出了next数组(先别管怎么求的).

当我们匹配完第(i)位,且第(i)位在模式串中对应的是(j),

(就像上面的例子中,(i)=5,(j)=5)

而在第(i)+1位失配了.

于是我们就要改变(j),使(i+1)能匹配上.

而改变的方式就是把(j)改为(next[j]).

想一想这是为什么.

假设有一个(k>next[j]),使(a[1sim k+1]=b[i+1-(k+1)+1sim i+1]).

(因为我们这里是在求(i)+1,而(j)是与(i)匹配,所以(k)加了1)

(k)对于(next[j])能使答案更优,

因为(i)是匹配上了的,所以(a[1sim j]=b[i-j+1sim i]).

因为(i+1)(k+1)匹配上了,所以(i)(k)也能匹配上,

(a[1sim k]=a[i-k+1sim i]).

因此我们可以发现(a[1sim k]=a[j-k+1sim j]).

画个图理解一下(前面的不理解的可以自己画下图):

红色区域代表匹配了的部分.

图片过于丑陋还请原谅.

回到正题,根据(next[j])的定义,这样的(k)是不存在的(否则(next[j])就等于(k))

因此直接跳到(next[j])肯定是更优的.

但是,(next[j])怎么求呢?

step 3

仔细康康(next)(f)的定义,是不是非常像?

没错,我们就可以像求(f)一样,直接求(next).

就相当于自己与自己匹配一次.

code:

inline void getnext(){
	Next[1]=0;
	for(int i=2,j=0;i<=la;i++){
		while(j>0&&a[i]!=a[j+1]) j=Next[j];
		if(a[i]==a[j+1]) j++;
		Next[i]=j;
	}
}

所以,这个问题就解决了!

总的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
using namespace std;

inline int read(){
	int sum=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return f*sum;
}

const int N=1000005;
int T,la,lb,f[N],Next[N],ans;
char a[N],b[N];

inline void getnext(){
	Next[1]=0;
	for(int i=2,j=0;i<=la;i++){
		while(j>0&&a[i]!=a[j+1]) j=Next[j];
		if(a[i]==a[j+1]) j++;
		Next[i]=j;
	}
}

inline void kmp(){
	for(int i=1,j=0;i<=lb;i++){
		while(j>0&&(j==la||b[i]!=a[j+1])) j=Next[j];
		if(b[i]==a[j+1]) j++;
		f[i]=j;if(f[i]==la) ans++;
	}
}

signed main(){
	T=read();
	while(T--){
		scanf("%s%s",a,b);
		la=strlen(a),lb=strlen(b);
		for(int i=la;i>=1;i--) a[i]=a[i-1];
		for(int i=lb;i>=1;i--) b[i]=b[i-1];
		getnext();kmp();		
		printf("%d
",ans);ans=0;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zsq259/p/11745658.html