KMP算法

这个算法我从2018-02-11在清北学堂的课上学了第1次,后在大二的数据结构课上学了第2次,然后又到现在学习了第3次,最终觉得自己理解了这个算法

本篇中我们以P代表模板串,就是在题目中通常长度较短的串

我们在S(模式串),也就是较长的串中,寻找P,并记录开始匹配和完成匹配的下标

这个算法的核心思想就是:每次失配后,我们并不是像暴力算法那样只前进一格,而是前进到这样一个位置:设在当前位置下部分匹配的字符串为A,我们将P串移动到仍然和这个A部分匹配的位置

为什么要移动到这个位置呢?

我们可以思考一下,往前移动会出现这样几种情况:

1.一点也不匹配,即P串的第一个位置和S就不匹配,这样的移动无疑是浪费,应该跳过

2.部分匹配,但是匹配的长度小于A,同理,这样的匹配不如匹配到A优

3.部分匹配(匹配长度>A的长度)或者完全匹配,这种情况必然是匹配到A的长度演化而来

综上,我们在一次失配后,移动到可以和本次成功匹配的前缀匹配的位置,这样的移动是正确的

如何确定每一次移动多少才能满足上述的条件呢?我们使用了next数组

next数组的含义是,p串中真前缀和真后缀(即不包括P串自己)相同的最大长度是多少

对于P="abcab",它的next数组为  

pabcab
i12345
next[i]0001

2

 

 

 

 

对于i=2,p="ab",前缀为a;后缀为b,两者不相等,next[2]=0

对于i=5,p="abcab",前缀为a,ab,abc,abca;后缀为b,ab,cab,bcab;可以看到最大相等的前缀后缀为ab,长度为2,next[5]=2

next[i]=j的含义就是:p[1...j] = p[i-j+1...j]

关于匹配过程:

这张图介绍了匹配的过程,比如开始我们部分匹配成功的为3串,我们接下来后移时,按照kmp的移位法则,我们需要尽可能向后移且使得1串=2串,也即是4串=2串(1串是由4串平移得到,所以1串=4串) ,这个移动距离我们就要使用next数组里的值了

那么如何求next数组呢?是通过模板串P自己匹配自己实现的,匹配操作几乎一样,设用Pj去匹配Pi,每次i++时,我们记录下此时Pj在Pi中的部分匹配的长度(即j走了多少)即可

kmp代码如下

#include <iostream>

using namespace std;

const int N=1e5+10,M=1e6+10;

char p[N],s[M];

int ne[M];

int main()
{
	int n,m;
	cin>>n>>p+1>>m>>s+1;
	
	//求next数组
    //以下的j应理解为,已经匹配完成的位置
	for(int i=2,j=0;i<=n;i++)
	{
		while(j && p[i]!=p[j+1]) j=ne[j];//如果失配就移位
        //判断j是否为0是因为,如果跳到了0的位置就不必再跳了
		if(p[i]==p[j+1]) j++;
		ne[i]=j;
	} 
	
	//匹配过程
	for(int i=1,j=0;i<=m;i++)
	{
		while(j && s[i]!=p[j+1]) j=ne[j];
		if(s[i]==p[j+1]) j++;
		if(j==n)
		{
			cout<<i-n<<' ';
			j=ne[j];
		}
	} 
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值