zoukankan      html  css  js  c++  java
  • P2414 [NOI2011]阿狸的打字机 AC自动机

    题意

    给定n个模式串,有m个询问,每次询问第X个模式串在第Y个模中出现了多少次

    解题思路

    以fail树相反的方向建一棵树T,问题转化为X的子树中有多少个y的终止节点。跑出T的dfs序,X的子树就可以表示为一段区间,然后对trie跑一遍dfs,每进入一个点就把dfs序列上对应的位置+1,离开一个点就把dfs序列上对应的位置-1,遍历到y的终止节点就计算所有Y=y的询问的结果,计算方法就是区间求和,用树状数组或线段树可以轻易地维护。

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int maxn=2e5+5;
    char s[maxn];
    struct Query{
    	int x,y,id,ans;
    	bool operator<(const Query& b)const{return y<b.y;}
    }q[maxn];
    int ql[maxn],qr[maxn],pos[maxn];
    
    
    
    //BIT
    int c[maxn];
    inline int lowbit(int x){return x&(-x);}
    void add(int x,int d){for(;x<maxn-5;x+=lowbit(x))c[x]+=d;}
    int getsum(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
    int getsum(int l,int r){return getsum(r)-getsum(l-1);}
    
    
    
    //AC_Automaton
    //root=0,range[1,tot]
    const static int SIZE=26; 
    int tr[maxn][SIZE],fail[maxn],tot;
    int fa[maxn],val[maxn],ch[maxn][SIZE];
    void insert(){
    	int p=0,cnt=0;
    	for(int i=0;s[i];i++){
    		if(s[i]=='P'){pos[++cnt]=p;val[p]=cnt;}
    		else if(s[i]=='B')p=fa[p];
    		else{
    			if(!tr[p][s[i]-'a'])tr[p][s[i]-'a']=++tot;
    			fa[tr[p][s[i]-'a']]=p;
    			p=tr[p][s[i]-'a'];
    		}
    	}
    	for(int i=0;i<=tot;i++)for(int j=0;j<26;j++)ch[i][j]=tr[i][j];
    }
    void getfail(){
    	queue<int>q;
    	for(int i=0;i<SIZE;i++)if(tr[0][i])fail[tr[0][i]]=0,q.push(tr[0][i]);
    	while(!q.empty()){
    		int p=q.front();q.pop();
    		for(int i=0;i<SIZE;i++){
    			if(tr[p][i]){
    				fail[tr[p][i]]=tr[fail[p]][i],q.push(tr[p][i]);
    			}
    			else tr[p][i]=tr[fail[p]][i];
    		}
    	}
    }
    
    
    
    
    //inv fail tree
    struct Edge{
    	int v,nxt;
    }e[maxn];
    int fi[maxn],se[maxn],sz;
    int head[maxn],num;
    void init_graph(){
    	memset(head,0,sizeof(head));
    	num=1;
    }
    void addedge(int u,int v){e[num].v=v;e[num].nxt=head[u];head[u]=num++;}
    void dfs_fail(int p){
    	fi[p]=++sz;
    	for(int i=head[p];i;i=e[i].nxt)dfs_fail(e[i].v);
    	se[p]=sz;	
    }
    
    
    
    //get ans
    int Ans[maxn];
    void dfs_trie(int p){
    	add(fi[p],1);
    	if(val[p]){
    		for(int i=ql[val[p]];i<=qr[val[p]];i++)
    			q[i].ans=getsum(fi[pos[q[i].x]],se[pos[q[i].x]]);
    	}
    	for(int i=0;i<26;i++)if(ch[p][i])dfs_trie(ch[p][i]);
    	add(fi[p],-1);
    }
    
    int main()
    {
    //#ifndef ONLINE_JUDGE
    //	freopen("in.txt","r",stdin);
    //#endif
    	scanf("%s",s);
    	insert();
    	
    	
    	getfail();
    	init_graph();
    	for(int i=1;i<=tot;i++)addedge(fail[i],i);
    	sz=0;dfs_fail(0);//建inv fail tree,跑出dfs序 
    	
    	
    	
    	int n;
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d %d",&q[i].x,&q[i].y);
    		q[i].id=i;q[i].ans=0;
    	}
    	sort(q+1,q+1+n);
    	for(int i=1,t=1;i<=n;i++){
    		ql[q[i].y]=i;
    		while(q[t+1].y==q[i].y)t++;
    		qr[q[i].y]=t;i=t;
    	}
    	dfs_trie(0);//遍历trie,跑出答案 
    	
    	for(int i=1;i<=n;i++)Ans[q[i].id]=q[i].ans;
    	for(int i=1;i<=n;i++)printf("%d\n",Ans[i]);
        return 0;
    }
    
    
  • 相关阅读:
    [SDOI2008]递归数列
    [SCOI2008]奖励关
    [SCOI2010]幸运数字
    [ZJOI2007]矩阵游戏
    [HAOI2006]旅行
    [ZJOI2008]泡泡堂
    [BZOJ1800][Ahoi2009]fly 飞行棋
    [POJ2288]Islands and Bridges
    [LUOGU] 3959 宝藏
    [BZOJ]1029: [JSOI2007]建筑抢修
  • 原文地址:https://www.cnblogs.com/zengzk/p/11415979.html
Copyright © 2011-2022 走看看