BZOJ 3172 TJOI 2013 单词 AC自动机

题目大意:给出一个由几个单词组成的文章,问每一个单词在文章中出现过多少次。


思路:应该是后缀数组把,但是我还不会,先用AC自动机水过去,但是发现一点都不水。。几乎调了一下午。

首先是用所有出现过的字符串构建一个AC自动机,然后要利用到刚才宽搜时候留下的数组中的宽搜序,按照这个宽搜序整理一下每个节点的cnt,然后最后按照顺序输出。

对于像我这种除了半平面交以外根本没手写过队列的弱渣来说,手写队列简直就是对我的摧残。。。


CODE:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 1000010
#define P(a) ((a) - 'a')
using namespace std;
 
struct Trie;
Trie *pointer[210];
 
struct Trie{
    Trie *son[27],*fail;
    int cnt;
     
    Trie() {
        cnt = 0;
        memset(son,NULL,sizeof(son));
    }
    void Insert(char *s,int p) {
        Trie *now = this;
        while(*s != '\0') {
            if(now->son[P(*s)] == NULL)
                now->son[P(*s)] = new Trie();
            now = now->son[P(*s)];
            ++s,++now->cnt;
        }
        pointer[p] = now;
    }
};
 
struct AC_Automation{
    Trie *root;
     
    AC_Automation() {
        root = new Trie();
        root->fail = NULL;
    }
    void Build(int cnt) {
        static char s[MAX];
        for(int i = 1; i <= cnt; ++i) {
            scanf("%s",s);
            root->Insert(s,i);
        }
        static Trie *q[MAX];
        int r = 0,h = 0;
        for(int i = 0 ; i < 26; ++i)
            if(root->son[i] != NULL) {
                root->son[i]->fail = root;
                q[++r] = root->son[i];
            }
        while(r != h) {
            Trie *now = q[++h];
            for(int i = 0; i < 26; ++i)
                if(now->son[i] != NULL) {
                    Trie *temp = now->fail;
                    while(temp != root && temp->son[i] == NULL)
                        temp = temp->fail;
                    if(temp->son[i] != NULL)
                        temp = temp->son[i];
                    now->son[i]->fail = temp;
                    q[++r] = now->son[i];
                }
        }
        while(r) {
            q[r]->fail->cnt += q[r]->cnt;
            --r;
        }
    }
}solver;
 
int points;
 
int main()
{
    cin >> points;
    solver.Build(points);
    for(int i = 1; i <= points; ++i)
        printf("%d\n",pointer[i]->cnt);
    return 0;
}


你可能感兴趣的:(后缀数组,AC自动机,bzoj,Tjoi2013,fail树)