这个算法用来干嘛
对于一个模板串M和一个子串S,n=|M|,m=|S|。
定义tend[i]为一个最大的k使得S[1..k]=M[i-k+1..i]。
也就是说M从第i位开始往前能与S的前缀最大匹配的长度。
M= a a b b a a b a a b a
S= a a b a a b b
如上例 tend[7]=3
KMP算法就是在线性的时间复杂度内计算tend[1..n]。
算法实现
在此之前,假设我们已经计算好了一个辅助数组next[1..m]
同样的,next[i]表示一个最大的k(k
代码模板
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i=a; i<=b; i++)
#define drep(i, a, b) for (int i=a; i>=b; i--)
#define inf 1e9
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
char S[maxn], P[maxn], next[maxn];
//P是模式串,S是匹配串
struct KMP {
int n, m, next[maxn], tend[maxn];//tend[i]表示匹配串从第i位开始往前能与S的前缀最大匹配的长度
int ans=0;
void makeNext() {
int q, k;
next[0] = 0;
for (q = 1, k = 0; q < m; q++) {
while (k > 0 && P[q] != P[k]) k = next[k - 1];
if (P[q] == P[k]) k++;
next[q] = k;
}
}
void init() {
n=strlen(S), m=strlen(P);
makeNext();
ans=0;
}
void kmp() {
init();
for (int i=0, q=0; i<n; i++) {
while (q>0 && P[q]!=S[i]) q=next[q-1];
if (P[q] == S[i]) q++;
tend[i]=q;
if (q==m) {
ans++;
//printf("Pattern occurs with shift:%d\n",(i-m+1));
//不可重叠加上 q=0;
}
}
}
}T;
int main() {
int kase;
scanf("%d", &kase);
while (kase--) {
scanf("%s%s", P, S);
T.kmp();
printf("%d\n", T.ans);
}
}
next数组的其它应用
next数组的一个妙用在于解决循环节问题,再此类问题中,其实不用完整做kmp,只需要求出next数组就够了
题目大意: 求这个字符串到i为止有多少个循环串
核心代码
for (int i = 0; i < n; i++) {
int k=i-T.next[i]+1;
if ((i+1)%k==0 && (i+1)!=k) printf("%d %d\n", i+1, (i+1)/k);
}
例如一个字符串的第100为指向第95位,也就是说4-100位和前1-96位是匹配的,就是说96到100与91到95是是匹配的,同理87-90与91-95是匹配的…依次类推,若100能整除4,则4即为一个循环节长度
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i=a; i<=b; i++)
#define drep(i, a, b) for (int i=a; i>=b; i--)
#define inf 1e9
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
char S[maxn], P[maxn], next[maxn];
//P是模式串,S是匹配串
struct KMP {
int n, m, next[maxn], tend[maxn];//tend[i]表示匹配串从第i位开始往前能与S的前缀最大匹配的长度
int ans=0;
void makeNext() {
int q, k;
next[0] = 0;
for (q = 1, k = 0; q < m; q++) {
while (k > 0 && P[q] != P[k]) k = next[k - 1];
if (P[q] == P[k]) k++;
next[q] = k;
}
}
void init() {
m = strlen(P);
makeNext();
ans = 0;
}
}T;
int main() {
freopen("in.txt", "r", stdin);
int kase=0, n;
while (scanf("%d", &n)!=EOF && n) {
scanf("%s", P);
T.init();
printf("Test case #%d\n", ++kase);
for (int i = 0; i < n; i++) {
int k=i-T.next[i]+1;
if ((i+1)%k==0 && (i+1)!=k) printf("%d %d\n", i+1, (i+1)/k);
}
printf("\n");
}
}
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7