前言
最近练习AC自动机,写个题解顺带存作板子。
题目描述
小t非常感谢大家帮忙解决了他的上一个问题。然而病毒侵袭持续中。在小t的不懈努力下,他发现了网路中的“万恶之源”。这是一个庞大的病毒网站,他有着好多好多的病毒,但是这个网站包含的病毒很奇怪,这些病毒的特征码很短,而且只包含“英文大写字符”。当然小t好想好想为民除害,但是小t从来不打没有准备的战争。知己知彼,百战不殆,小t首先要做的是知道这个病毒网站特征:包含多少不同的病毒,每种病毒出现了多少次。大家能再帮帮他吗?
Input
第一行,一个整数 ( ),表示病毒特征码的个数。
接下来N行,每行表示一个病毒特征码,特征码字符串长度在1—50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。
在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。
(PS:有多组数组,出题人压根没提到这点)
分析
对于这道题,我们要统计每种串分别出现的次数,因此在统计时就不必把节点上的val标记抹去,再把答案存到一个cnt数组中就好。
参考程序
// vjudge 244506 B
#include <cstdio>
#include <cstring>
const int MAXN = 1005;
char P[MAXN][55], Source[2000005];
int N, cnt[MAXN];
namespace AC_automaton {
const int TOT_LEN = 50005;
int sz, T[TOT_LEN][26], fail[TOT_LEN], end[TOT_LEN];
// end表示在此结尾的模式串编号,0就是没有
inline void init() { // 初始化自动机
sz = 1, end[0] = 0;
memset(T[0], 0, sizeof(T[0]));
}
void insert_pattern(const char * st, int id); // 插入模式串
void construct(); // 构造自动机
void match(const char * st); // 匹配
}
void solve();
int main() {
while (scanf("%d", &N) == 1 && N) solve();
return 0;
}
void solve() {
AC_automaton::init();
for (int i = 1; i <= N; i++) {
scanf("%s", P[i]);
AC_automaton::insert_pattern(P[i], i);
}
AC_automaton::construct(); // 一开始没写构造函数居然过了样例???惊奇!
scanf("%s", Source);
AC_automaton::match(Source);
}
namespace AC_automaton {
void insert_pattern(const char * st, int id) {
int rt = 0;
for (int i = 0, ch; st[i]; rt = T[rt][ch], ++i)
if (!T[rt][ch = st[i] - 'A']) end[sz] = 0, memset(T[sz], 0, sizeof(T[sz])), T[rt][ch] = sz++;
end[rt] = id;
}
int Que[TOT_LEN];
void construct() {
int i, hd = 0, tl = 0;
for (i = 0; i < 26; i++)
if (T[0][i]) Que[tl++] = T[0][i], fail[T[0][i]] = 0;
while (hd < tl) {
int rt = Que[hd++];
for (i = 0; i < 26; i++) {
int & now = T[rt][i];
if (now) fail[now] = T[fail[rt]][i], Que[tl++] = now;
else now = T[fail[rt]][i];
}
}
}
void match(const char * st) {
memset(cnt, 0, sizeof(cnt));
for (int i = 0, rt = 0; st[i]; ++i)
if (st[i] < 'A' || st[i] > 'Z') rt = 0; // 注意会有非法字符出现,这时候一定是失配,直接回到根节点
else for (int now = rt = T[rt][st[i] - 'A']; now; now = fail[now]) ++cnt[end[now]];
for (int i = 1; i <= N; i++)
if (cnt[i]) printf("%s: %d\n", P[i], cnt[i]);
}
}