[BZOJ2434][Noi2011]阿狸的打字机 AC自动机+树状数组+离线
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2434
题目中这种多个串匹配的问题,一下子就想到了AC自动机。然后发现如果要建立AC自动机,跟着题目中的方式,不用把每个串提出来。如果是一个普通字符就直接加进去,如果是P就把当前节点记录下来,代表一个串,如果是B就相当于退回到trie树中的父亲节点。
建好AC自动机后来观察一下题目中的问题。这个询问相当于统计从根节点到代表y串的那个节点上的路径上,有多少个节点可以通过跳fail指针的方式到达x串的节点。
暴力统计显然是不行的,观察到fail路径上的每一个点的出度都为1,那么将fail全部反过来就是一棵树。问题就变成了在x的子树中,有多少个从根节点到y串的节点的路径上的节点。把fail树的dfs序求出来,我们可以用树状数组求子树和。
考虑离线。那么重新模拟题目中打字的过程,如果加入了一个字符,对应树状数组中此节点dfs序的位置+1,如果被删除了,则-1。这样就能实时维护树状数组中的点全部是y串中的点,于是提前处理一下,如果有关于y串的询问,答案就是x串的子树和,也就是dfs序对应的区间和。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int inline readint(){ 6 int Num;char ch; 7 while((ch=getchar())<'0'||ch>'9');Num=ch-'0'; 8 while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0'; 9 return Num; 10 } 11 void outint(int x){ 12 if(x>=10) outint(x/10); 13 putchar(x%10+'0'); 14 } 15 char s[100010]; 16 int Len,M; 17 int ch[100010][26],fa[100010],sz=0; 18 int pos[100010],tot=0; 19 void Build_trie(){ 20 int now=0; 21 for(int i=1;i<=Len;i++){ 22 if(s[i]>='a'&&s[i]<='z'){ 23 int idx=s[i]-'a'; 24 if(!ch[now][idx]) ch[now][idx]=++sz; 25 fa[ch[now][idx]]=now; 26 now=ch[now][idx]; 27 } 28 else if(s[i]=='B') now=fa[now]; 29 else pos[++tot]=now; 30 } 31 } 32 int to[100010],ne[100010],fir[100010],cnt=0; 33 void Addedge(int a,int b){ 34 to[++cnt]=b; 35 ne[cnt]=fir[a]; 36 fir[a]=cnt; 37 } 38 int fail[100010],q[100010]; 39 void Set_fail(){ 40 memset(fir,-1,sizeof(fir)); 41 int head=1,tail=1; 42 q[1]=0; 43 while(head<=tail){ 44 int now=q[head]; 45 for(int i=0;i<26;i++){ 46 if(ch[now][i]){ 47 q[++tail]=ch[now][i]; 48 int tmp=now?ch[fail[now]][i]:0; 49 fail[ch[now][i]]=tmp; 50 Addedge(tmp,ch[now][i]); 51 } 52 else ch[now][i]=ch[fail[now]][i]; 53 } 54 head++; 55 } 56 } 57 int dfn=0,in[100010],out[100010]; 58 void Dfs(int x){ 59 in[x]=++dfn; 60 for(int i=fir[x];i!=-1;i=ne[i]) Dfs(to[i]); 61 out[x]=dfn; 62 } 63 struct Query{ 64 int x,y,idx; 65 bool operator < (const Query &_)const{ 66 return y<_.y; 67 } 68 }qry[100010]; 69 int tree[100010]; 70 int inline lowbit(int &x){ 71 return x&-x; 72 } 73 void Add(int x,int d){ 74 while(x<=dfn){ 75 tree[x]+=d; 76 x+=lowbit(x); 77 } 78 } 79 int Sum(int x){ 80 int sum=0; 81 while(x){ 82 sum+=tree[x]; 83 x-=lowbit(x); 84 } 85 return sum; 86 } 87 int Ans[100010]; 88 int main(){ 89 scanf("%s",s+1); 90 Len=strlen(s+1); 91 Build_trie(); 92 Set_fail(); 93 Dfs(0); 94 M=readint(); 95 for(int i=1;i<=M;i++){ 96 qry[i].x=readint(); 97 qry[i].y=readint(); 98 qry[i].idx=i; 99 } 100 sort(qry+1,qry+1+M); 101 int now=0,sk=0,qk=1; 102 for(int i=1;i<=Len;i++){ 103 if(s[i]>='a'&&s[i]<='z'){ 104 now=ch[now][s[i]-'a']; 105 Add(in[now],1); 106 } 107 else if(s[i]=='B'){ 108 Add(in[now],-1); 109 now=fa[now]; 110 } 111 else{ 112 sk++; 113 while(qry[qk].y==sk){ 114 Ans[qry[qk].idx]=Sum(out[pos[qry[qk].x]])-Sum(in[pos[qry[qk].x]]-1); 115 qk++; 116 } 117 } 118 } 119 for(int i=1;i<=M;i++){ 120 outint(Ans[i]); 121 putchar('\n'); 122 } 123 return 0; 124 }