KMP
模板
KMP 的思想主要是有一个 kmp 数组,然后记录匹配失败后,需要移动到哪里。
每一个位置只需要进行一次比较。
然后代码比较直观。
代码
C++
#include<bits/stdc++.h>
#define int long long
using namespace std;
char s[1000010],t[1000010];
int kmp[1000010];
signed main(){
cin>>(s+1)>>(t+1);
int n=strlen(s+1),m=strlen(t+1);
int j=0;
for(int i=2;i<=m;i++){
while(j&&t[j+1]!=t[i])j=kmp[j];
if(t[i]==t[j+1])j++;
kmp[i]=j;
}
j=0;
for(int i=1;i<=n;i++){
while(j&&s[i]!=t[j+1])j=kmp[j];
if(t[j+1]==s[i])j++;
if(j==m){
cout<<i-m+1<<'\n';
j=kmp[j];
}
}
for(int i=1;i<=m;i++)cout<<kmp[i]<<' ';
cout<<'\n';
return 0;
}
扩展 KMP
P5410 【模板】扩展 KMP / exKMP(Z 函数)。
这篇 TJ 讲的清楚,C 过来。
结论: 对于 ,对任意 都可以递推得:
证明:
主要思想是将这个位置转换成前面的一个位置加上一个位移,然后这个位置的 函数我们是知道的,只需要移动到 就可以转换成下一个函数了。
初始化 ,然后暴力判断字符相等增加 。
这里 选满足 最大的 ,这样每个字符只会被暴力判断一次(如果最小值是 就会第一次就失配),所以时间复杂度可以做到 。
对于题目中的问题其实把 和 接起来做个 就可以了。
最小表示法
先断环成链放两个。
先放 和 ,表示最小的和当前的(下标小的设置为最小的)。
然后如果 是最小的,然后现在有来了一个 ,先把能匹配的 位匹配。
如果 更小, 就变成 ,因为这中间的一定比 到 中间的某一个大(他们匹配了一些位置了)。
如果 更小, 变成 。
复杂度易证。
代码
C++
#include<bits/stdc++.h>
using namespace std;
int n;
char s[20000010];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
cin>>(s+1);
for(int i=1;i<=n;i++){
s[i+n]=s[i];
}
int i=1,j=2;
for(;i<=n&&j<=n;){
int k=0;
while(k<=n&&s[i+k]==s[j+k])k++;
if(s[i+k]>s[j+k]) i+=k+1;
else j+=k+1;
if(i==j)j++;//不要漏
}
int ans=min(i,j);
for(int i=ans;i<=ans+n-1;i++)cout<<s[i];
cout<<endl;
return 0;
}