码迷,mamicode.com
首页 > 其他好文 > 详细

●HDU 4787 GRE Words Revenge

时间:2018-03-10 20:34:36      阅读:157      评论:0      收藏:0      [点我收藏+]

标签:cpp   target   string   case   重构   turn   16px   相加   进入   

题链:

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

标签:cpp   target   string   case   重构   turn   16px   相加   进入   

原文地址:https://www.cnblogs.com/zj75211/p/8541598.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!