标签:bzoj noi2007 字符串 ac自动机 fail树
题目大意:有一种打字机上有28个字母,分别是26个小写字母和BP,其中B代表退格,P代表换行,每一行就是一个字符串。现在给这些字符串标号,并询问x串在y串中出现过几次。
思路:这算是NOI史上最难的字符串的题了吧(动物园)。
首先按照题意不难建一个AC自动机出来,按照正常的思路,对于每一个询问都需要在AC自动机上暴力的查找。但这样时间会十分好看。
于是我们想,fail指针构成的一定是一棵树,将这颗树的DFS序搞出来的话会有非常好的性质--串中存在某个字串的一定在这个字串的子树中,也就是对应DFS序的一段区间里。这样我们吧所有询问离线,对整个trie树进行一次深搜,用fenwick来维护DFS序中的情况,顺便处理出来答案。
CODE:
#include <queue> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 using namespace std; #define P(a) ((a) - 'a') struct Trie{ Trie *son[26],*fail; int end; Trie() { memset(son,NULL,sizeof(son)); fail = NULL; } }mempool[MAX],*C = mempool + 1,*root = C++; int asks; int fenwick[MAX]; int head[MAX],total; int next[MAX],aim[MAX]; int pos[MAX],p[MAX]; pair<int,int> range[MAX]; vector<pair<int,int> > G[MAX]; int ans[MAX]; inline void Add(int x,int y) { next[++total] = head[x]; aim[total] = y; head[x] = total; } void BuildTrie() { static Trie *stack[MAX],*now = root; int top = 0,cnt = 0; char c; stack[++top] = root; while(c = getchar(),isalpha(c)) { if(c == 'P') { now->end = ++cnt; p[now - mempool] = cnt; } else if(c == 'B') now = stack[top--]; else { stack[++top] = now; if(now->son[P(c)] == NULL) now->son[P(c)] = C++; now = now->son[P(c)]; } } } void BuildACAutomation() { static queue<Trie *> q; while(!q.empty()) q.pop(); q.push(root); while(!q.empty()) { Trie *now = q.front(); q.pop(); for(int i = 0; i < 26; ++i) { if(now->son[i] == NULL) continue; if(now == root) { now->son[i]->fail = root; Add(root - mempool,now->son[i] - mempool); } else { 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; Add(temp - mempool,now->son[i] - mempool); } q.push(now->son[i]); } } } void Pre(int x) { static int k = 0; pos[x] = ++k; if(p[x]) range[p[x]].first = k; for(int i = head[x]; i; i = next[i]) Pre(aim[i]); if(p[x]) range[p[x]].second = k; } inline void Fix(int x,int c) { for(; x < MAX; x += x&-x) fenwick[x] += c; } inline int GetSum(int x) { int re = 0; for(; x; x -= x&-x) re +=fenwick[x]; return re; } void DFS(int x) { Trie *now = &mempool[x]; if(pos[x]) Fix(pos[x],1); for(vector<pair<int,int> >::iterator it = G[p[x]].begin(); it != G[p[x]].end(); ++it) { pair<int,int> now = *it; ans[now.second] = GetSum(range[now.first].second) - GetSum(range[now.first].first - 1); } for(int i = 0; i < 26; ++i) if(now->son[i] != NULL) DFS(now->son[i] - mempool); if(pos[x]) Fix(pos[x],-1); } int main() { BuildTrie(); BuildACAutomation(); cin >> asks; for(int x,y,i = 1; i <= asks; ++i) { scanf("%d%d",&x,&y); G[y].push_back(make_pair(x,i)); } Pre(1); DFS(1); for(int i = 1; i <= asks; ++i) printf("%d\n",ans[i]); return 0; } ?
BZOJ 2434 NOI 2011 阿狸的打字机 AC自动机构造fail树
标签:bzoj noi2007 字符串 ac自动机 fail树
原文地址:http://blog.csdn.net/jiangyuze831/article/details/41846483