[BZOJ2434][NOI2011]阿狸的打字机
2016-02-08 16:25
330 查看
发现一种新的思路,以前从来没有见过的,即AC自动机的fail树。
这一题我们先考虑暴力,从root往Y的最后一个点走,如果走到了X的末点,ans++,如果通过fail指针走到了X的末点,ans++。
反过来考虑,从X的末点开始,如果当前点在Y串或者通过反向的Fail到了Y串,ans++。
又发把fail反向之后得到的是一棵树,那么也就是要求在X末点的子树里有多少个Y点,转化为DFS序后用树状数组维护即可。
这一题我们先考虑暴力,从root往Y的最后一个点走,如果走到了X的末点,ans++,如果通过fail指针走到了X的末点,ans++。
反过来考虑,从X的末点开始,如果当前点在Y串或者通过反向的Fail到了Y串,ans++。
又发把fail反向之后得到的是一棵树,那么也就是要求在X末点的子树里有多少个Y点,转化为DFS序后用树状数组维护即可。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<vector> #include<queue> #define X first #define Y second #define LL long long #define MP make_pair #define lowbit(x) (x&(-x)) #define DEBUG(...) fprintf(stderr,__VA_ARGS__) using namespace std; const int MAXN=1e5+10; char s[MAXN]; int n; int first[MAXN],e=0,next[MAXN],to[MAXN],sum[MAXN*3],ans[MAXN]; void add(int a,int b) { ++e;next[e]=first[a];first[a]=e;to[e]=b; } void _add(int x,int d) { while(x<=2*MAXN) { sum[x]+=d; x+=lowbit(x); } } int query(int x) { int res=0; while(x>0) { res+=sum[x]; x-=lowbit(x); } return res; } struct ACM{ int son[MAXN][27],fa[MAXN],tot,val[MAXN],f[MAXN],FA[MAXN]; int _first[MAXN],_next[MAXN],_e,_to[MAXN],l[MAXN],r[MAXN],dfs_clock; ACM(){tot=0;memset(son,0,sizeof(son));memset(fa,0,sizeof(fa));memset(FA,0,sizeof(FA));memset(f,0,sizeof(f));memset(val,0,sizeof(val));dfs_clock=0;} int idx(char c){return c-'a';} void insert(char* S) { int N=strlen(S),u=0,k=0; for(int i=0;i<N;i++) { if(S[i]>='a'&&S[i]<='z') { int x=idx(S[i]); if(!son[u][x]) { son[u][x]=++tot; FA[tot]=u; u=tot; } else u=son[u][x]; } else if(S[i]=='P') val[++k]=u; else if(S[i]=='B') u=FA[u]; } } void getFail() { queue<int> q; f[0]=0; for(int i=0;i<26;i++) { int u=son[0][i]; if(u) { q.push(u);f[u]=0; } } while(!q.empty()) { int r=q.front();q.pop(); for(int i=0;i<26;i++) { int u=son[r][i]; if(!u)continue; q.push(u); int j=f[r]; while(j&&!son[j][i])j=f[j]; f[u]=son[j][i]; } } } void Add(int a,int b) { ++_e;_next[_e]=_first[a];_first[a]=_e;_to[_e]=b;fa[b]=a; } void buildTree() { _e=0; memset(_first,-1,sizeof(first)); fa[0]=0; queue<int> q; for(int i=0;i<26;i++) { int u=son[0][i]; if(u)q.push(u); } while(!q.empty()) { int u=q.front();q.pop(); Add(f[u],u); for(int i=0;i<26;i++) { int v=son[u][i]; if(!v)continue; q.push(v); } } memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); } void DFS(int u) { l[u]=++dfs_clock; for(int i=_first[u];i!=-1;i=_next[i]) { int v=_to[i]; DFS(v); } r[u]=++dfs_clock; } void solve() { int N=strlen(s),u=0,now,cnt=0; for(int i=0;i<N;i++) { if(s[i]>='a'&&s[i]<='z') { now=son[u][idx(s[i])]; _add(l[now],1); u=now; //DEBUG("+%d %d\n",u,l[u]); } else if(s[i]=='P') { ++cnt; if(cnt==8) cnt=8; for(int i=first[cnt];i!=-1;i=next[i]) { int v=to[i]; ans[i]=query(r[val[v]])-query(l[val[v]]-1); } } else { _add(l[u],-1); //DEBUG("-%d %d\n",u,l[u]); u=FA[u]; } } } }ac; int main() { #ifndef ONLINE_JUDGE freopen("type.in","r",stdin); freopen("type.out","w",stdout); #endif scanf("%s",s); memset(first,-1,sizeof(first)); ac.insert(s); ac.getFail(); ac.buildTree(); ac.DFS(0); scanf("%d",&n); for(int i=1;i<=n;i++) { int x,y; scanf("%d %d",&x,&y); add(y,x); } ac.solve(); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); }
相关文章推荐
- java项目经验总结之自定义异常类
- C#成神之路<15> C#异常处理调试
- [LeetCode]39. Combination Sum
- 矩阵乘法经典应用之坐标变化
- volley中多级别取消请求Request
- 中科院ICTCLA授权文件更新网址
- 注解和反射
- IT痴汉的工作现状44-外包困局
- 标准C++中的string类的用法总结
- nyoj 1112
- 1013. 数素数
- 数值的整数次方
- 安装交叉工具链
- ADO.NET之利用存储过程录入数据
- SSH 是什么
- Android算命小程序的实现
- 【翻译自mos文章】对于一个新建的db来说,对Dba_Feature_Usage_Statistics的查询返回零行
- python map、filter、reduce
- java 变长參数使用原则
- JavaScript 实现页面元素(ul-li)的简单排序