• [bzoj2434][Noi2011]阿狸的打字机——AC自动机


    题目大意:

    给定n个字符串,每次询问第x个字符串在第y个字符串中出现了多少次。

    思路:

    显然我们需要先把AC自动机给建出来。
    考虑如何最暴力地计算第x个字符在第y个字符中出现了多少次,我们可以在Trie上暴力跳y的每一个节点,然后对于y的每一个节点跳fail,如果跳到了x串的结尾,那么答案+1。
    这样对于每一个节点都跳fail显然复杂度无法接受,那么换一个角度考虑,x的结尾可以被y串上的多少个节点给跳到。
    于是我们可以把每个节点的fail看成是它的父亲,然后问题就转化为了求x的结尾点的子树中包含了多少个y的节点,这样以后可以暴力标记y上的每一个节点,对于x直接子树求和即可。
    这样以后,对于y相同的二元组就可以同时处理了,相当于把x挂在了y上。
    但是这样复杂度还是太高,无法接受,于是我们发现复杂度主要在暴力标记y上的每一个节点上,于是考虑将询问按照y离线,把y挂在串y的结尾点上,然后按照dfs的顺序遍历Trie树上的每一个节点,进入这个节点时打上标记,回溯时将标记撤回,这样到了y的结尾的时候,不难发现此时打上标记的点恰好是y串上所有的点。

    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
    #define debug(x) cout<<#x<<"="<<x<<" "
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("bzoj2434.in","r",stdin);
    	freopen("bzoj2434.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	_=0; T f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    	for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    	_*=f;
    }
    
    const int maxn=1e5+10;
    int m;
    
    int ch[maxn][26],num[maxn],fail[maxn],cnt;
    int fa[maxn],le[maxn],pos[maxn];
    vector<int>rank[maxn];
    
    void print(int u){
    	if(fa[u]!=1)print(fa[u]);
    	putchar(le[u]+'a');
    }
    
    void init(){
    	fail[cnt=1]=1;
    
    	char s[maxn];
    	scanf("%s",s+1);
    
    	int len=strlen(s+1),u=1,c,cnt_rank=0;
    	REP(i,1,len){
    		if(s[i]=='B')u=fa[u];
    		else if(s[i]=='P'){
    			++num[u];
    			rank[u].pb(++cnt_rank);
    			pos[cnt_rank]=u;
    		}
    		else{
    			c=s[i]-'a';
    			if(!ch[u][c])ch[u][c]=++cnt;
    			fa[ch[u][c]]=u,le[ch[u][c]]=c;
    			u=ch[u][c];
    		}
    	}
    }
    
    void build_fail(){ int h=1,t=0,q[maxn];
    	REP(i,0,25)if(ch[1][i]){
    		fail[ch[1][i]]=1;
    		q[++t]=ch[1][i];
    	}
    
    	while(h<=t){
    		int u=q[h++];
    		REP(i,0,25)if(ch[u][i]){
    			int v=ch[u][i],p=fail[u];
    			q[++t]=v;
    			while(p!=1 && !ch[p][i])p=fail[p];
    			if(ch[p][i])fail[v]=ch[p][i];
    			else fail[v]=1;
    		}
    	}
    }
    
    int beg[maxn],to[maxn],las[maxn],cnte,dfn[maxn],cnt_dfn,sz[maxn];
    
    void add(int u,int v){
    	las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;
    }
    
    void dfs_dfn(int u){
    	dfn[u]=++cnt_dfn;
    	sz[u]=1;
    	for(int i=beg[u];i;i=las[i]){
    		dfs_dfn(to[i]);
    		sz[u]+=sz[to[i]];
    	}
    }
    
    struct BIT{
    	int sum[maxn];
    	int lowbit(int x){
    		return x&(-x);
    	}
    	void modify(int p,int x){
    		for(;p<=cnt;p+=lowbit(p))sum[p]+=x;
    	}
    	int query(int p){
    		int ret=0;
    		for(;p>=1;p-=lowbit(p))ret+=sum[p];
    		return ret;
    	}
    }T;
    
    int ans[maxn];
    vector<pii>qu[maxn];
    
    void solve(int u){
    	T.modify(dfn[u],1);
    
    	REP(i,0,rank[u].size()-1){
    		int y=rank[u][i];
    		REP(j,0,qu[y].size()-1){
    			int x=pos[qu[y][j].fi];
    			ans[qu[y][j].se]=T.query(dfn[x]+sz[x]-1)-T.query(dfn[x]-1);
    		}
    	}
    
    	REP(i,0,25)if(ch[u][i]){
    		int v=ch[u][i];
    		solve(v);
    	}
    	T.modify(dfn[u],-1);
    }
    
    int main(){
    	File();
    
    	init();
    
    	build_fail();
    
    	REP(i,2,cnt)add(fail[i],i);
    	dfs_dfn(1);
    
    	int x,y;
    	read(m);
    	REP(i,1,m){
    		read(x),read(y);
    		qu[y].pb(mk(x,i));
    	}
    
    	solve(1);
    	REP(i,1,m)printf("%d
    ",ans[i]);
    
    	return 0;
    }
    
    
  • 相关阅读:
    【转】最大子序列和(动态规划学习)
    [转]修改Oracle XDB的8080端口
    【转】 C++常见编译/链接错误及其解决办法
    Pentaho Dashboard Editor使用向导
    [转]什么是Unicode是什么是UTF8是什么
    【转】 typedef的四个用途和两个陷阱
    【转】 C++中类型转换的解释
    从一道笔试题谈算法优化
    [转]谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词
    [转]对Oracle数据库的字符集问题的资料收集,受益匪浅
  • 原文地址:https://www.cnblogs.com/ylsoi/p/10182087.html
Copyright © 2020-2023  润新知