自动AC机就是trie上的kmp,由于trie从跟到任意节点都是模式串中存在的一个子串,所以可以类比KMP,从每个节点向另一个节点连一条失配边(KMP的失配边就是next数组,因此可视为trie为一条链)。实现的过程中有一点优化,就是如果一个点的某个儿子不存在,把他连向fail指针的该儿子。这样就不用写循环了,清爽了很多。
模板题:hdu2222
题意:给定一个大字符串和若干模式串,求有多少模式串在大字符串中出现过。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1000005;
const int MAXS = 10005 * 50;
int T, N;
char s1[55], s[MAXN];
int que[MAXS];
struct Trie
{
int ch[MAXS][26], cnt[MAXS], fail[MAXS];
bool vis[MAXS];
int ncnt;
void init()
{
for (int i = 0; i<26; ++i)
ch[0][i] = 1;
for (int i = 1; i<=ncnt; ++i) {
for (int j = 0; j<26; ++j) ch[i][j] = 0;
vis[i] = 0, fail[i] = cnt[i] = 0;
}
ncnt = 1;
}
void ins(char *s)
{
int p = 1;
for (int i = 0, c; s[i]; ++i)
{
c = s[i] - 'a';
if (!ch[p][c]) p = ch[p][c] = ++ncnt;
else p = ch[p][c];
}
++cnt[p];
}
void build()
{
int l = 0, r = 0, t, p, i;
fail[que[l] = 1] = 0;
while (l <= r) {
t = que[l ++];
for (i = 0; i < 26; ++i) {
if (!ch[t][i]) continue;
p = fail[t];
while (!ch[p][i]) p = fail[p];
fail[ch[t][i]] = ch[p][i];
que[++r] = ch[t][i];
}
}
}
int quary(char *s)
{
int k = 1, c, ans = 0;
for (int i = 0; s[i]; ++i) {
vis[k] = 1;
c = s[i] - 'a';
while (!ch[k][c]) k = fail[k];
k = ch[k][c];
if (!vis[k])
for (int j = k; j; j = fail[j])
ans += cnt[j], cnt[j] = 0;
}
return ans;
}
} ac;
int main()
{
scanf("%d", &T);
while (T--) {
scanf("%d", &N);
ac.init();
while (N --)
scanf("%s", s1), ac.ins(s1);
ac.build();
scanf("%s", s);
printf("%d\n", ac.quary(s));
}
return 0;
}