题目链接
题意:中文题我就不多说什么了
以前也做过几个AC自动机+数据结构优化的题目,都是建好fail树然后就跟ac自动机无关了,所以这种题目都必须深刻理解AC自动机的原理以及fail树是怎么回事。
第一步:先读入字符串,建好AC自动机,我在这里被坑了,,,根据题目的特殊性,可以O(n)插入到字典树里面
第二步:建成fail树,预处理dfs序。
第三步: 离线回答询问。
联想暴力的算法,遍历每一个单词,每到一个节点,就一直fail fail,如果出现x的尾节点,就+1,。但这样子太暴力了,我们可以想一下,一直fail fail的过程,不就像一直往父亲节点的方向走么,如果将fail指针指向的点当成父亲几点建出一棵树,判断x在y中出现了几次,不就是判断字典树中root->y的路径中有几个点存在于x所在尾节点的子树中么,动态统计一棵子树中有几个点,就需要用数据结构来维护了。具体做的时候可以碰到一个Y的尾节点就处理所有的 对应的x的询问。
虽然调了好久,但其实是水题啊!!
#include<stdio.h> #include<string.h> #include<vector> #include<algorithm> using namespace std; const int M = 100010; const int CD = 26; struct BIT{ int c[M]; void init() { memset(c,0,sizeof(c)); } void update(int x,int d){ for(;x<M;x+=x&-x) c[x] += d; } int sum(int x) { int ans = 0; for(;x>0;x-=x&-x) ans += c[x]; return ans; } }bit; struct Fail_tree { vector<int> Eg[M]; int L[M] , R[M] ; int tfn ; void add(int a,int b) { Eg[a].push_back(b); } void dfs(int a) { L[a] = ++tfn; for(vector<int>::iterator it = Eg[a].begin();it!=Eg[a].end();it++) dfs(*it); R[a] = tfn; } }FT; bool vis[M]; struct ACM { void init() { words = 0; fail[0] = 0; val[0] = 0; memset(ch[0],0,sizeof(ch[0])); sz = 1; for(int i = 0; i < 26; i++) ID[i+'a'] = i; } void Insert(char *s) { int p=0; for(;*s;s++){ if(*s == 'P') { ++words; hash[words] = p; name[p].push_back(words); } else if(*s == 'B') { p = fa[p]; } else { int c = ID[*s]; if(!ch[p][c]){ memset(ch[sz],0,sizeof(ch[sz])); val[sz]=0; fa[sz] = p; ch[p][c]=sz++; } p=ch[p][c]; } } } void Construct() { int *s=Q,*e=Q; for(int i=0;i<CD;i++){ if(ch[0][i]){ fail[ch[0][i]] = 0; *e++ = ch[0][i]; } } while(s!=e){ int u = *s++; for(int i=0;i<CD;i++){ int &v = ch[u][i]; if(v){ *e++ = v; fail[v]=ch[fail[u]][i]; } else { v=ch[fail[u]][i]; } } } } void build() { init(); Insert(str); Construct(); FT.tfn = 0; for(int i = 1; i < sz; i++) FT.add(fail[i],i) ; FT.dfs(0); } void solve() { build(); bit.init(); int p = 0; for(int i = 0; str[i]; i++){ if(str[i] == 'P') { if(vis[p]) continue; vis[p] = true; int tsz = name[p].size(); for(int j = 0; j < tsz ; j++) { int y = name[p][j]; int ysz = edge[y].size(); for(int k = 0; k < ysz; k ++) { int word = edge[y][k].first ; int id = edge[y][k].second; ans[id] = bit.sum(FT.R[hash[word]]) - bit.sum(FT.L[hash[word]] - 1) ; } } } else if(str[i] == 'B') { bit.update(FT.L[p],-1); p = fa[p]; } else { p = ch[p][ID[str[i]]] ; bit.update(FT.L[p],1); } } for(int i = 0; i < m; i++) printf("%d\n",ans[i]); } void input() { int x , y; scanf("%s",str); scanf("%d",&m); for(int i = 0; i < m; i++) { scanf("%d%d",&x,&y); edge[y].push_back(make_pair(x,i)); } } vector<int> name[M]; int hash[M]; int ans[M]; vector<pair<int,int> > edge[M]; int words; int tot; char ss[M]; int m; int ID[128]; int fail[M]; int sz; int ch[M][CD]; int Q[M]; int val[M]; int fa[M]; char str[M]; }AC; int main() { AC.input(); AC.solve(); return 0; }