KMP

基础

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

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

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

考虑 \(t\)abababc。那么 \(ne\) 数组为 \(0,0,1,2,3,4,0\)

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

有一个显然的结论,\(ne_{i+1}-ne_i\le 1\),这个就不证明了。

于是我们可以分类讨论,对 \(t_{i+1}\) 是否等于 \(t_{{ne_i}+1}\) 进行分类。

  • 相等,此时 \(ne_{i+1}={ne_i}+1\)

  • 否则,我们考虑令 \(j=ne_{ne_i}\),判断 \(t_{j+1}\) 是否等于 \(t_{i+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\),然后求一下 \(t\)\(ne\) 数组,然后不加前后缀相同的那部分就行了。

于是你超时了。

为什么呢?如果第一个字符串非常长,你还每次都跑一遍,那么复杂度为 \(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 @ 2024-07-28 20:28  zxh923  阅读(5)  评论(0编辑  收藏  举报