KMP算法

String 字符串

KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
KMP最重要的就是求next数组,也是最麻烦的一个地方,先上代码,慢慢说明:next数组的求解代码如下:

void get_next(string s){
	int k = 0;
	next[0] = 0;
	for(int i=1;i<s.length();i++){
		while(k!=0 && s[i]!=s[k]){
			k = next[k];
		}
		if(s[k]==s[i]) k++;
		next[i] = k;
	}
//	for(int i=0;i<s.length();i++){
//		cout << next[i] << " ";
//	}
}

我们在这里以模式串为abcab为例,我们手写next数组的结果就是:
0 0 0 1 2,这是什么意思呢,next[i]代表的含义就是以第 i 个字符为最后一位时后缀与前缀最长的相同部分,这里可能有些拗口,我们多举几个例子:
0:我们初始化next[0] = 0,因为如果第一个都不一样了我们就直接比较下一个即可;
1:ab: 前缀有 a,后缀有 b ,很显然没有相同的部分,所以next[1] = 0;
2:abc:前缀有a,ab,后缀有c,bc,很显然也没有相同的部分,所以next[2] = 0;
3:abca:前缀有a,ab,abc,后缀有a,ca,bca,显然前缀中的“a”和后缀中的“a”相同,所以前缀与后缀最长的长度为1,所以next[3] = 1;
4:abcab:前缀有a,ab,abc,abca,后缀有b,ab,cab,bcab,显然前缀与后缀中最长的相同部分是“ab”,长度是2,所以next[4] = 2;
这样我们就求完了这样一个完整的next数组,可能有些同学就问了这个有什么用,我们这样看,如果有一个字符串为abababc,模式串为ababc我们比较了前四个abab是相同的,只是第五个不一样,所以我们如果用朴素的比较的话又重新从原串的第二个b进行比较,我们之前做的四次比较将毫无用处,然而真的没有用处吗?我们发现模式串的前四个abab中前两个与后两个是相同的,所以我们没有必要再比较一遍,因为我们知道从第三个开始的ab和从第一个开始的ab是相同的,所以我们直接跳过前两个的比较,从第三个开始比较就好:
在这里插入图片描述
完整代码如下:
给一个模式串s和原串s1,来查找模式串在原串中出现的次数:

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxn = 1e5+10;
int next[maxn];
void get_next(string s){
	int k = 0;
	next[0] = 0;
	for(int i=1;i<s.length();i++){
		while(k!=0 && s[i]!=s[k]){
			k = next[k];
		}
		if(s[k]==s[i]) k++;
		next[i] = k;
	}
//	for(int i=0;i<s.length();i++){
//		cout << next[i] << " ";
//	}
}
void KMP(string s,string s1){
	int sum = 0;
	int k = 0;
	for(int i=0;i<s1.length();){
		while(k<s.length() && i+k<s1.length() && s1[i+k]==s[k]) 
			k++;
		if(k>=s.length()) sum++;
		else if(k) k--;
		if(k<s.length() && next[k]) i += next[k];
		else i++;
		k = next[k];
	}
	cout << sum << endl;
}
void solve(){
	memset(next,0,sizeof(next));
	string s,s1;
	int n,m;
	cin >> n >> m; //n是s的长度,m是s1的长度
	cin >> s >> s1;
	for(int i=0;i<s1.length();i++){
		if(s1[i]>='A'&&s1[i]<='Z') s1[i] = s1[i] - 'A' + 'a';
	}
//	cout << s1 << endl;
	get_next(s);
	KMP(s,s1);
}
int main(){
	int t;
	cin >> t;
	while(t--){
		solve();
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值