BZOJ 3172 [Tjoi2013]单词 AC自动机Fail树

题目链接:【http://www.lydsy.com/JudgeOnline/problem.php?id=3172】

题意:给出一个文章的所有单词,然后找出每个单词在文章中出现的次数,单词用标点符号隔开。

题解:时间很坑啊,朴素的做法是,先在AC自动机上插入单词,并记录下每个单词,然后用每个单词去维护答案。但是这样做时间空间都不允许,只有在插入的时候维护答案。过程是这样的,在插入的时候,每经过一个点,该节点的值加一,同时记录单词末尾的位置,然后在求玩Fail的时候,倒着走回去就可以了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2024 * 1024 + 15;
char tmp[maxn];
struct Aho_C
{
    int Next[maxn][26], Fail[maxn], pos[maxn], cnt[maxn];
    int que[maxn], st, ed;
    int root, sz;
    int newnode()
    {
        for(int i = 0; i < 26; i++) Next[sz][i] = 0;
        cnt[sz] = 0;
        sz++;
        return sz - 1 ;
    }
    void init()
    {
        sz = 0;
        root = newnode();
    }
    void Insert(char buf[], int id)
    {
        int len = strlen(buf);
        int now = root;
        for(int i = 0; i < len; i++)
        {
            if(!Next[now][buf[i] - 'a'])
                Next[now][buf[i] - 'a'] = newnode();
            now = Next[now][buf[i] - 'a'];
            cnt[now]++;
        }
        pos[id] = now;
    }
    void getFail()
    {
        st = ed = 1;
        for(int i = 0; i < 26; i++)
            if(Next[root][i])
                que[ed++] = Next[root][i];
        while(st != ed)
        {
            int u = que[st++];
            for(int i = 0; i < 26; i++)
            {
                int &v = Next[u][i];
                if(!v)
                {
                    v = Next[Fail[u]][i];
                    continue;
                }
                Fail[v] = Next[Fail[u]][i];
                que[ed++] = v;
            }
        }
        for(int i = ed - 1; i >= 1; i--) cnt[Fail[que[i]]] += cnt[que[i]];
    }
} ac;
int main()
{
    int N;
    scanf("%d", &N);
    ac.init();
    for(int i = 1; i <= N; i++)
    {
        scanf("%s", tmp);
        ac.Insert(tmp, i);
    }
    ac.getFail();
    for(int i = 1; i <= N; i++) printf("%d\n", ac.cnt[ac.pos[i]]);
    return 0;
}

  

posted @ 2017-10-01 21:10  _Mickey  阅读(140)  评论(0编辑  收藏  举报