【学习笔记】KMP
因为字符串对我太抽象了,所以只能以水的心态写这个 KMP。
感觉考到字符串的题肯定要崩。
以下均规定字符串 \(S\) 以下标 0 开头。
前缀函数:
给定一个长度为 \(n\) 的字符串 \(S\),其前缀函数被定义为一个长度为 \(n\) 的数组 \(\pi\)。
简单来说 \(\pi[i]\) 就是,子串 \(s[0\dots i]\) 最长的相等的真前缀(即不包括字符串 \(s\) 本身的前缀)与真后缀的长度。
周期:
若 \(S[i]=S[i+p]\) 对 \(i=0,\ldots,n-1-p\) 均成立,则 \(p\) 是 \(S\) 的一个周期。\((1\leq p\leq n)\)
border:
若 \(S[ : r) = S[-r: )\),则 \(r\) 是 \(S\) 的一个 border 的长度。\((0\leq r< n)\)
注意到存在 border \(r\) 等价于存在周期 \(p= N-r\)。
可以发现 \(\pi[n]\) 就 是 最 大 的 border。
进一步地 \(S\) 的所有 border 为 \(\pi[n], \pi[\pi[n]], \pi[\pi[\pi[n]]], \ldots\)
换句话说,利用 \(\pi\) 可以求出 \(S\) 的所有周期。
思路(求 \(\pi\) 数组):
-
对于 \(1\leq i<n\),有 \(\pi[i+1]\leq\pi[i]+1\)。
实际上 \(\pi[i+1]=\pi[i]+1\) 当且仅当 \(S[i]=S[\pi[i]]\)。 -
对于 \(S[i]\neq S[\pi[i]]\) 的情形,我们需要找到最大的 \(j<\pi[i]\) 使得 \(S[:j)=S[i-j:i)\) 且 \(S[i]=S[j]\)。
-
可以不断地令 \(j\leftarrow\pi[j]\) 直至找到满足 \(S[:j)=S[i-j:i)\) 且 \(S[i]=S[j]\) 的 \(j\)。
附赠字符串下标从 1 开始的 KMP模板:(绝对不是因为从 0 开始写挂了)
\(\pi\) 数组即为 \(nxt\) 数组。
注意如何比对两个字符串。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6+5;
int nxt[N];
int main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
string s1, s2; cin>>s1>>s2;
int len1 = s1.length(), len2 = s2.length();
s1 = ' '+s1, s2 = ' '+s2;
for(int i=2; i<=len2; i++){
nxt[i] = nxt[i-1];
while(nxt[i] && s2[nxt[i]+1]!=s2[i]) nxt[i] = nxt[nxt[i]];
if(s2[nxt[i]+1]==s2[i]) nxt[i]++;
}
for(int i=1, j=0; i<=len1; i++){
// j 是已经匹配完的模式串的最后一位的位置
while(j && s1[i]!=s2[j+1]) j=nxt[j];
if(s1[i]==s2[j+1]) j++;
if(j==len2){
cout<<i-len2+1<<"\n";
j = nxt[j]; // 继续匹配
}
}
for(int i=1; i<=len2; i++)
cout<<nxt[i]<<" ";
return 0;
}
因为我太菜了,所以 0 下标开始的只能附赠巨佬 @ysl_wf 的代码:
%%%%%%%%%%%%stO巨佬Orz%%%%%%%%%%%%%%
#include<bits/stdc++.h>
using namespace std;
typedef long long lt;
const lt N = 1e6 + 10;
lt pi[N];
lt la, lb, j;
char a[N], b[N];
int main(){
cin >> a;
cin >> b;
la = strlen(a), lb = strlen(b);
j = -1; pi[0] = -1;
for(int i = 1; i < lb; i++){
while(j >= 0 && b[i] != b[j+1])
j = pi[j];
if(b[j+1] == b[i])
j++;
pi[i] = j;
}
j = -1;
for(int i = 0; i < la; i++){
while(j >= 0 && b[j+1] != a[i])
j = pi[j];
if(b[j+1] == a[i])
j++;
if(j == lb-1){
printf("%lld\n", i-lb+2);
j = pi[j];
}
}
for(int i = 0; i < lb; i++){
printf("%lld ", pi[i]+1);
}
}
标签:lb,int,笔记,学习,++,KMP,字符串,pi
From: https://www.cnblogs.com/FlyPancake/p/18335290/KMP