AC 自动机学习笔记
P3808 【模板】AC自动机(简单版)
给定
n 个模式串s_i 和一个文本串t ,求有多少个不同的模式串在文本串里出现过。
题意可以转化为:有多少模式串是文本串的子串。
子串不就是前缀的后缀吗?
对模式串建立
考虑枚举文本串的一个前缀
考虑对于
引入转移的概念:我们称
显然,通过这样的转移我们可以建立一个
我们考虑怎么求出
显然有,如果知道
那么通过一遍 bfs 则可以把
比较时即可通过
放个代码:
#include <stdio.h>
#include <queue>
#include <string>
#include <iostream>
#define Maxn 1000005
#define Rep(i, l, r) for(i = l; i <= r; ++ i)
int n;
struct acm{
int m[Maxn][26], cnt[Maxn], mu[Maxn], tot;
std :: queue <int> q;
void ins(std :: string s) {
int len = s.length(), x, y;
for(x = 0, y = 0; y < len; ++ y) {
if(! m[x][s[y] - 'a']) m[x][s[y] - 'a'] = ++ tot;
x = m[x][s[y] - 'a'];
}
++ cnt[x];
}
void build() {
int i, u, v;
Rep(i, 0, 25) if(m[0][i]) q.push(m[0][i]);
while(! q.empty()) {
u = q.front(), q.pop();
Rep(v, 0, 25) {
if(m[u][v]) mu[m[u][v]] = m[mu[u]][v], q.push(m[u][v]); //处理儿子的 mu
else m[u][v] = m[mu[u]][v]; // 继承下转移
}
}
}
int match(std :: string s) {
int len = s.length(), ans = 0;
for(int x = 0, y = 0, u; y < len; ++ y) {
x = m[x][s[y] - 'a'];
for(int v = x; v && cnt[v] != -1; v = mu[v]) {
ans += cnt[v];
cnt[v] = -1;
}
}
return ans;
}
}T;
int main() {
int i;
std :: string s;
scanf("%d", &n);
Rep(i, 1, n) std :: cin >> s, T.ins(s);
T.build(); std :: cin >> s;
printf("%d", T.match(s));
}
updated on 2023.10.20
感觉写的还行,好像是我前期写的最有感觉的一篇。