KMP

基础

下文的字符串下标皆从 1 开始。

考虑定义一个数组 nei,指的是设字符串 t 的前 i 位为 s。字符串 s 的前 nei 位与后 nei 位完全相同,且 nei 取到了最大值,并且 nei 不为字符串 s 的长度。

是不是觉得很绕?我们举一个例子来更好的说明:

考虑 tabababc。那么 ne 数组为 0,0,1,2,3,4,0

然后我们考虑如何快速求出 ne 数组。暴力枚举当然是一种方案,但是复杂度我们不能接受,于是我们考虑优化。

有一个显然的结论,nei+1nei1,这个就不证明了。

于是我们可以分类讨论,对 ti+1 是否等于 tnei+1 进行分类。

  • 相等,此时 nei+1=nei+1

  • 否则,我们考虑令 j=nenei,判断 tj+1 是否等于 ti+1 并重复这一过程。

为什么在否则时我们的做法是正确的,可以看一下图:

然后这样做的时间复杂度是线性的,可以看一下题目。

题目

KMP

板子题,可以直接看一下代码:

#include<bits/stdc++.h>
#define int long long
#define N 1000005
using namespace std;
int ne[N],j,len1,len2;
char s1[N],s2[N];
signed main(){
	cin>>(s1+1)>>(s2+1);
	len1=strlen(s1+1);len2=strlen(s2+1);
	for(int i=2;i<=len2;i++){
		while(j&&s2[i]!=s2[j+1])j=ne[j];
		if(s2[i]==s2[j+1])j++;
		ne[i]=j;
	}
	j=0;
	for(int i=1;i<=len1;i++){
		while(j&&s1[i]!=s2[j+1])j=ne[j];
		if(s1[i]==s2[j+1])j++;
		if(j==len2){
			cout<<i-len2+1<<'\n';
			j=ne[j];
		}
	}
	for(int i=1;i<=len2;i++)cout<<ne[i]<<' ';
	return 0;
}

Compress Words

幽默题,坑点很多。

考虑设当前的答案为 res,输入的新字符串为 s,那么考虑字符串 t=s+res,然后求一下 tne 数组,然后不加前后缀相同的那部分就行了。

于是你超时了。

为什么呢?如果第一个字符串非常长,你还每次都跑一遍,那么复杂度为 O(nm) 左右,其中 m 为字符串长度和。

所以要优化,设当前 res 长度 m,你发现 ne 数组不会超过 s 的长度 len,于是你只需要取 res 的后 min(m,len) 位去组成新字符串 t

于是你又挂了。

为什么呢?看这样一个数据:101 010,你求出来的 ne 数组跨越了两个字符串,这是不被允许的,所以你可以在拼接时中间加个特殊字符,就可以过了。

代码:

#include<bits/stdc++.h>
#define int long long
#define N 1000005
using namespace std;
int n,ne[N];
string s,t,res;
void kmp(string s){
	int len=s.size();
	s=' '+s;
	ne[0]=ne[1]=0;
	int j=0;
	for(int i=2;i<=len;i++){
		while(j&&s[i]!=s[j+1])j=ne[j];
		if(s[i]==s[j+1])j++;
		ne[i]=j;
	}
}
signed main(){
	cin>>n;
	while(n--){
		cin>>s;
		int len=min(s.size(),res.size());
		t=s+'|'+res.substr(res.size()-len,len);
		kmp(t);
		for(int i=ne[t.size()];i<s.size();i++){
			res+=s[i];
		}
	}
	cout<<res;
	return 0;
}
posted @   zxh923  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示