bzoj2434 阿狸的打字机[NOI2011] AC自动机+树状数组
2018-02-05 12:37
274 查看
如果你还没学AC自动机,请看这篇博客
洛谷通道
首先可以想到很暴力的做法,就是模拟操作构造出AC自动机,然后对于每次询问暴力匹配,然而只能拿到50分……
考虑优化:
我们知道AC自动机是依靠前缀树Trie构造出来的,要判断字符串v是否是u的子串,就只需要判断v是否是u的某一个前缀的后缀(模拟一下你会发现很显然)。而怎么判断v是不是x的后缀呢,就是判断能否从x沿着fail链走到v。
那么询问就转化为了从虚根到u的路径上,有多少个节点可以沿着fail指针跳到v的结束节点。那么我们就可以根据fail指针建立一棵fail树,以fail[x]作为x节点的父节点,这样的话如果i是j的祖先,显然j就可以走到i。为了方便统计,可以对于询问的y,在fail树上,将root到y的路径都变为1,然后统计以x为根的子树的总权值即可。
但是尽管如此,暴力修改还是不能AC,因为修改实在太耗时了,我们希望降低修改的复杂度。对fail树进行dfs得到dfs序,并且得到以x为根的子树范围l[x]~r[x]。然后用树状数组进行维护,为了方便修改我们就对询问根据y进行排序,然后模拟操作即可。详见solve()函数。
adb4
Problem
bzoj通道洛谷通道
Solution
简单的说来,其实就是要快速求一个字符串在另一个字符串中出现了多少次。考虑构造AC自动机。首先可以想到很暴力的做法,就是模拟操作构造出AC自动机,然后对于每次询问暴力匹配,然而只能拿到50分……
考虑优化:
我们知道AC自动机是依靠前缀树Trie构造出来的,要判断字符串v是否是u的子串,就只需要判断v是否是u的某一个前缀的后缀(模拟一下你会发现很显然)。而怎么判断v是不是x的后缀呢,就是判断能否从x沿着fail链走到v。
那么询问就转化为了从虚根到u的路径上,有多少个节点可以沿着fail指针跳到v的结束节点。那么我们就可以根据fail指针建立一棵fail树,以fail[x]作为x节点的父节点,这样的话如果i是j的祖先,显然j就可以走到i。为了方便统计,可以对于询问的y,在fail树上,将root到y的路径都变为1,然后统计以x为根的子树的总权值即可。
但是尽管如此,暴力修改还是不能AC,因为修改实在太耗时了,我们希望降低修改的复杂度。对fail树进行dfs得到dfs序,并且得到以x为根的子树范围l[x]~r[x]。然后用树状数组进行维护,为了方便修改我们就对询问根据y进行排序,然后模拟操作即可。详见solve()函数。
Code
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <queue> #define cl(x) memset((x),0,sizeof((x))) #define lowbit(x) (x)&(-(x)) using namespace std; const int maxn=100010; int n,p,dfc,len,ans[maxn],head[maxn],l[maxn],r[maxn],c[maxn]; char s[maxn]; queue<int> q; struct data{ int v,nxt; }edge[maxn]; struct node{ int x,y,id; bool operator < (const node &t) const { return y<t.y; } }a[maxn]; void add(int pos,int v){for(;pos<=dfc;pos+=lowbit(pos)) c[pos]+=v;} int sum(int pos) { int res=0; for(;pos;pos-=lowbit(pos)) res+=c[pos]; return res; } struct acam{ int sz,ch[maxn][26],pre[maxn],f[maxn],pos[maxn]; void clear(){sz=0;cl(ch);cl(f);cl(pos);} void build(char *c) { clear(); //sz=0; int now=0,cnt=0; for(int i=0;i<len;i++) { if(s[i]=='P') pos[++cnt]=now;//pos映射第i个串的结束节点 else if(s[i]=='B') now=pre[now]; else { int p=c[i]-'a'; if(!ch[now][p]){ch[now][p]=++sz;pre[sz]=now;} now=ch[now][p]; } } } void getfail() { while(!q.empty()) q.pop(); q.push(0); while(!q.empty()) { int u=q.front(),fa;fa=f[u]; q.pop(); for(int i=0;i<26;i++) { int v=ch[u][i]; if(!v){ch[u][i]=ch[fa][i];continue;} f[v]=u?ch[fa][i]:0; q.push(v); } } } void solve() { int k=1,cnt=0,now=0; for(int i=0;i<len;i++) { if(s[i]=='P') for(cnt++;a[k].y==cnt&&k<=n;k++) { int tmp=pos[a[k].x]; ans[a[k].id]=sum(r[tmp])-sum(l[tmp]-1); } else if(s[i]=='B') add(l[now],-1),now=pre[now]; else now=ch[now][s[i]-'a'],add(l[now],1); } } }ac; inline void insert(int u,int v){edge[++p]=(data){v,head[u]};head[u]=p;} void dfs(int x)//l[x]~r[x]:dfs序中子树x的范围 { l[x]=r[x]=++dfc; for(int i=head[x];i;i=edge[i].nxt) { dfs(edge[i].v); r[x]=r[edge[i].v]; } } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif scanf("%s",s); len=strlen(s); ac.build(s);ac.getfail(); for(int i=1;i<=ac.sz;i++) insert(ac.f[i],i);//构建fail树 dfs(0); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&a[i].x,&a[i].y); a[i].id=i; } sort(a+1,a+n+1); ac.solve(); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }
adb4
相关文章推荐
- [BZOJ]2434: [Noi2011]阿狸的打字机 AC自动机+树状数组
- 【BZOJ2434】阿狸的打字机(NOI2011)-AC自动机+树状数组
- bzoj 2434: [Noi2011]阿狸的打字机 AC自动机+树状数组
- 【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组
- 【BZOJ2434-[Noi2011]】阿狸的打字机(AC自动机(fail树)+离线+树状数组)
- 【AC自动机-fail树+离线+DFS序+树状数组】BZOJ2434(Noi2011)[阿狸的打字机]题解
- [BZOJ2434][NOI2011]阿狸的打字机(AC自动机+树状数组)
- bzoj2434 [Noi2011]阿狸的打字机
- BZOJ 2434: [Noi2011]阿狸的打字机
- 【BZOJ 2434】[Noi2011]阿狸的打字机 Ac自动机+树状数组+dfs序
- BZOJ2434: [Noi2011]阿狸的打字机
- [省选前题目整理][BZOJ 2434][NOI 2011]阿狸的打字机(AC自动机+fail树+DFS序+树状数组)
- [BZOJ2434]NOI2011阿狸的打字机|AC自动机|fail树|树状数组
- BZOJ 2434 [Noi2011]阿狸的打字机(AC自动机)
- BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)
- ●BZOJ 2434: [Noi2011]阿狸的打字机
- 【dfs序+AC自动机+树状数组】BZOJ2434-[Noi2011]阿狸的打字机
- NOI2011 阿狸的打字机(BZOJ2434) 题解
- BZOJ 2434: [Noi2011]阿狸的打字机 AC自动机 fail树
- [JZOJ2784][BZOJ2434]【NOI2011】阿狸的打字机