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; }
想的太多,做的太少。