• BZOJ 3881: [Coci2015]Divljak


    3881: [Coci2015]Divljak

    Time Limit: 20 Sec  Memory Limit: 768 MB
    Submit: 553  Solved: 176
    [Submit][Status][Discuss]

    Description

    Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的。
    接下来会发生q个操作,操作有两种形式:
    “1 P”,Bob往自己的集合里添加了一个字符串P。
    “2 x”,Alice询问Bob,集合T中有多少个字符串包含串S_x。(我们称串A包含串B,当且仅当B是A的子串)
    Bob遇到了困难,需要你的帮助。
     
     

    Input

    第1行,一个数n;
    接下来n行,每行一个字符串表示S_i;
    下一行,一个数q;
    接下来q行,每行一个操作,格式见题目描述。

    Output

    对于每一个Alice的询问,帮Bob输出答案。

    Sample Input

    3
    a
    bc
    abc
    5
    1 abca
    2 1
    1 bca
    2 2
    2 3

    Sample Output

    1
    2
    1

    HINT

    【数据范围】

    1 <= n,q <= 100000;

    Alice和Bob拥有的字符串长度之和各自都不会超过 2000000;

    字符串都由小写英文字母组成。

    Source

    分析:

    好神的题目...OTZ...

    我们要求的是一个串在多少个串中出现了,这个问题可以用fail树来解决...

    我们先拿出所有的$S$集合中的串建AC自动机,然后把fail树拎出来,每一次我们在$P$集合中新添加一个串的时候就把这个串在AC自动机上去匹配,把匹配的节点记录下来,这些节点在fail树上到根节点的路径上的所有点在$P$集合中的出现次数都增加了1,但是要去重,怎么办?树连剖分?复杂度爆炸...于是有一个机智的做法,我们把每一节点到根节点的路径的上的点的权值都+1,然后把点按照dfs序排序,把相邻两个节点的$lca$到根节点的路径上的点的权值全部-1...但是如果真的路径修改单点查询很麻烦,所以我们把权值的加减标记全部累加的节点上,每次询问的时候统计子树的标记和,用树状数组+dfs序维护就好了...

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    //by NeighThorn
    using namespace std;
    
    const int maxm=2000000+5,maxn=100000+5;
    
    int n,m,cnt,dfn,tot,top,head,tail,q[maxm],fa[maxm][25],be[maxm],en[maxm],hd[maxm],to[maxm],nxt[maxm],dep[maxm],stk[maxm],node[maxn];
    
    char s[maxm],t[maxm];
    
    struct trie{
    	int fail,nxt[26];
    }tr[maxm];
    
    struct Tree{
    	
    	int tree[maxm];
    	
    	inline void insert(int x,int y){
    		for(;x<=dfn;x+=x&-x)
    			tree[x]+=y;
    	}
    	
    	inline int query(int x){
    		int res=0;
    		for(;x;x-=x&-x)
    			res+=tree[x];
    		return res;
    	}
    	
    }T;
    
    inline void add(int x,int y){
    	to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
    }
    
    inline void insert(char *s,int id){
    	int p=0,len=strlen(s);
    	for(int i=0;i<len;i++){
    		if(!tr[p].nxt[s[i]-'a'])
    			tr[p].nxt[s[i]-'a']=++tot,tr[tot].fail=-1;
    		p=tr[p].nxt[s[i]-'a'];
    	}
    	node[id]=p;
    }
    
    inline void buildACM(void){
    	head=0,tail=0,q[0]=0;
    	while(head<=tail){
    		int id=q[head++],p=-1;
    		for(int i=0;i<26;i++){
    			if(tr[id].nxt[i]){
    				if(id){
    					p=tr[id].fail;
    					while(p!=-1){
    						if(tr[p].nxt[i]){
    							tr[tr[id].nxt[i]].fail=tr[p].nxt[i];
    							break;
    						}
    						p=tr[p].fail;
    					}
    					if(p==-1) tr[tr[id].nxt[i]].fail=0;
    				}
    				else
    					tr[tr[id].nxt[i]].fail=0;
    				q[++tail]=tr[id].nxt[i];
    			}
    			else if(id)
    				tr[id].nxt[i]=tr[tr[id].fail].nxt[i];
    		}
    	}
    }
    
    inline void dfs(int root){
    	be[root]=++dfn;
    	for(int i=hd[root];i!=-1;i=nxt[i])
    		dep[to[i]]=dep[root]+1,fa[to[i]][0]=root,dfs(to[i]);
    	en[root]=dfn;
    }
    
    inline void init(void){
    	for(int j=1;j<=20;j++)
    		for(int i=1;i<=tot;i++)
    			fa[i][j]=fa[fa[i][j-1]][j-1];
    }
    
    inline int LCA(int x,int y){
    	if(dep[x]<dep[y])
    		swap(x,y);
    	int d=dep[x]-dep[y];
    	for(int i=0;i<=20;i++)
    		if((d>>i)&1)
    			x=fa[x][i];
    	if(x==y)
    		return x;
    	for(int i=20;i>=0;i--)
    		if(fa[x][i]!=fa[y][i])
    			x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    
    inline bool cmp(int x,int y){
    	return be[x]<be[y];
    }
    
    signed main(void){
    	memset(hd,-1,sizeof(hd));
    	scanf("%d",&n);tr[0].fail=-1;
    	for(int i=1;i<=n;i++)
    		scanf("%s",s),insert(s,i);
    	buildACM();scanf("%d",&m);
    	for(int i=1;i<=tot;i++)
    		add(tr[i].fail,i);
    	fa[0][0]=0,dfs(0);init();
    	for(int C=1,opt,x;C<=m;C++){
    		scanf("%d",&opt);
    		if(opt==1){top=0;
    			scanf("%s",t);int p=0;
    			for(int i=0;t[i];i++){
    				p=tr[p].nxt[t[i]-'a'];
    				if(p)
    					stk[++top]=p;
    				else
    					break;
    			}
    			sort(stk+1,stk+top+1,cmp);
    			int tmp=top;top=0;
    			for(int i=1;i<=tmp;i++)
    				if(stk[i]!=stk[i-1])
    					stk[++top]=stk[i];
    			for(int i=1;i<=top;i++)
    				T.insert(be[stk[i]],1);
    			for(int i=2;i<=top;i++)
    				T.insert(be[LCA(stk[i],stk[i-1])],-1);
    		}
    		else{
    			scanf("%d",&x);x=node[x];
    			printf("%d
    ",T.query(en[x])-T.query(be[x]-1));
    		}
    	}
    	return 0;
    }
    

      


    By NeighThorn

  • 相关阅读:
    mvc生成table
    JQ仿ebay右侧flash商品展示
    调查一下,EF的Bug?
    SpringCloud组件Zuul入门解析
    SpringCloud组件Ribbon入门解析
    FTO介绍
    算法网址收藏
    哈拂大学凌晨四点的景象
    【系统学习ES6】第二节:解构赋值
    MySQL高级
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6408095.html
Copyright © 2020-2023  润新知