HDU3065 病毒侵袭持续中

前言

最近练习AC自动机,写个题解顺带存作板子。

题目描述

小t非常感谢大家帮忙解决了他的上一个问题。然而病毒侵袭持续中。在小t的不懈努力下,他发现了网路中的“万恶之源”。这是一个庞大的病毒网站,他有着好多好多的病毒,但是这个网站包含的病毒很奇怪,这些病毒的特征码很短,而且只包含“英文大写字符”。当然小t好想好想为民除害,但是小t从来不打没有准备的战争。知己知彼,百战不殆,小t首先要做的是知道这个病毒网站特征:包含多少不同的病毒,每种病毒出现了多少次。大家能再帮帮他吗?

Input

第一行,一个整数N1\leq N\leq1000),表示病毒特征码的个数。
接下来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]);
    }
}

About The Author

发表评论

电子邮件地址不会被公开。 必填项已用*标注