题链:
http://acm.hdu.edu.cn/showproblem.php?pid=4787
题解:
AC自动机(强制在线构造)
题目大意:
有两种操作,
一种为:+S,表示增加模式串S,
另一种为:?S,表示查询S中有多少子串为已经给出的模式串。
(同时由于输入根据上一次的答案加密 ,所以强制在线)
(事先提一下,对于多次给出的相同模式串,是要去重的,至于怎么去重,就随便用trie树或者map+string就好了。)
进入正题:
难道真的要让AC自动机变得在线起来么?
其实还是用普通AC自动机做的。
即每次插入一个新的串,我们都重构AC自动机。
当然,为了保证时间复杂度,我们采取用两个AC自动机的做法:
不妨把两个AC自动机分别叫做A和B。
每次对于新来的模式串,我们把它加入B,并重构B自动机。
但B这个自动机有一个大小限制M,
一旦B的节点大小大于了M,我们就把B自动机的串全部放到A里面去,并清空B自动机。
然后对于每个询问,我们只需要在A,B里都分别求得答案并相加即可。
而至于复杂度,就是由B自动机的那个节点数目限制M决定的。
而M的取值为sqrt(模式串总长)时,复杂度就比较好了。
不妨假设所有模式串长度为len,全部插入trie树后节点也有len个。
由于每个新来的模式串都会重构B,而B的大小为sqrt(len),即重构代价为O(sqrt(len))
所以在B上花费的总复杂度为:O(串的个数n*sqrt(len))
由于每当B的大小为sqrt(len)时,就会把里面的串放入A,并重构A。
而A的大小最大就是len,上述的"把B里面的串放入A"的操作最多只会进行len/sqrt(len)=sqrt(len)次。
所以在A上花费的总复杂度为:O(len*sqrt(len))
所以实现这个在线构造AC自动机的复杂度为O(len*sqrt(len)+串的个数n*sqrt(len))
但是由于往往模式串的个数n不是很多,所以我们定义的M可以比sqrt(len)大一些,从而减小时间消耗 。
代码:
#include<bits/stdc++.h> #define MAXN 100005 #define BSIZE 2000//320 #define rint register int using namespace std; int Case,N; map<string,int>H; string Str; struct ACAM{ int size; bool tag[MAXN]; int ch[MAXN][2],fail[MAXN],sum[MAXN]; void Reset(){ for(rint i=1;i<=size;i++) tag[i]=ch[i][0]=ch[i][1]=0; size=1; } int Insert(int p,int c){ if(!ch[p][c]) ch[p][c]=++size; return ch[p][c]; } void Getfail(){ static queue<int> Q; Q.push(1); fail[1]=0; while(!Q.empty()){ int u=Q.front(); Q.pop(); tag[u]|=tag[fail[u]]; for(int c=0;c<=1;c++){ int p=fail[u]; while(p&&!ch[p][c]) p=fail[p]; if(!ch[u][c]) ch[u][c]=p?ch[p][c]:1; else{ int v=ch[u][c]; fail[v]=p?ch[p][c]:1; Q.push(v); sum[v]=tag[v]+sum[fail[v]]; } } } } void Build(char *S){ Reset(); int p=1; for(rint i=0;S[i];i++){ if(S[i]==‘+‘){ if(i!=0&&S[i-1]!=‘+‘) tag[p]=1; p=1; } else p=Insert(p,S[i]-‘0‘); } tag[p]=1; Getfail(); } int Match(char *T){ static int p,ret,q; p=1; ret=0; if(size==1) return ret; for(rint i=0;T[i];i++){ if(T[i]==‘?‘) p=1; else p=ch[p][T[i]-‘0‘]; ret+=sum[p]; } return ret; } }A,B; char S[MAXN*52]; void decode(int br,int key,char *T){ static int len,p; len=strlen(T); //key=0; for(int i=0,p=key%len;i<len;i++){ S[br+i]=T[p]; p++; if(p>=len) p-=len; } S[br+len]=0; } int main(){ static char T[MAXN*52]; scanf("%d",&Case); int ar,br,newlen,ans=0; for(int C=1;C<=Case;C++){ H.clear(); printf("Case #%d:\n",C); scanf("%d",&N); ans=ar=br=0; A.Reset(); B.Reset(); for(int i=1;i<=N;i++){ scanf(" %c",&S[br]); S[++br]=0; scanf("%s",&T[0]); decode(br,ans,T); br--; if(S[br]==‘?‘){ ans=0; ans+=A.Match(&S[br]); ans+=B.Match(&S[br]); printf("%d\n",ans); } else{ Str=&S[br]; if(H[Str]==1) continue; H[Str]=1; newlen=strlen(&S[br]); br+=newlen; if(B.size>=BSIZE){ A.Build(&S[0]); B.Reset(); ar=br; } else B.Build(&S[ar]); } } } return 0; }