1. 程式人生 > >●HDU 4787 GRE Words Revenge

●HDU 4787 GRE Words Revenge

r+ %d size decode struct pop 就是 trie樹 pos

題鏈:

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;
}

  

●HDU 4787 GRE Words Revenge