zoukankan      html  css  js  c++  java
  • ●HDU 4787 GRE Words Revenge

    题链:

    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:
    ",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
    ",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;
    }
    

      

  • 相关阅读:
    Android 数据库SQLite的使用简单Demo
    jQuery插件FullCalendar日程表
    Activiti工作流访问路径资源注册和管理
    电子商务中的数据仓库技术
    打开”我的电脑”慢的解决办法
    数据结构定义中的中(大陆地区)美差异
    Wubi离线安装Ubuntu
    C++如何获得U盘的盘符以及U盘相关属性的代码
    浮动引起的文本重影
    定位相关的怪异问题
  • 原文地址:https://www.cnblogs.com/zj75211/p/8541598.html
Copyright © 2011-2022 走看看