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

    [NOI2011]阿狸的打字机

    这题可以大大加深我们对自动AC机的理解。

    一上来:哇,这是什么神仙读入啊,如果按照它的要求一个一个把所有串建出来的话,肯定会MLE呀!

    后来想一想,它这么读入,肯定构成一棵树,并且,它刚好是字典树!

    这里是建树的方法。

    void ins(){
    	int x=1;
    	for(int i=0;i<S;i++){
    		if(s[i]=='P')t[x].fin.push_back(++n),pos[n]=x;
    		else if(s[i]=='B'){
    			if(x==1)continue;
    			x=t[x].fa;
    		}else{
    			if(!t[x].ch[s[i]-'a'])t[x].ch[s[i]-'a']=++cnt,t[cnt].fa=x,t[x].org[s[i]-'a']=true;
    			x=t[x].ch[s[i]-'a'];
    		}
    	}
    }
    

    注意到这里有一个将串映射到trie中节点的数组(pos),以后有用。

    然后就不会了,看了题解。

    我们必须明确一点,就是一个节点的所有祖先(fail)内,包含了它的全部出现过的前缀。

    因此,对于询问((x,y)),我们找到(x)串的结尾节点,则该节点在(fail)树中的子树内,有多少个节点出现在了(y)串中,则询问((x,y))的答案就是多少。

    因为对于每一个这样的节点,它总可以通过多次跳(fail)跳到(x)串的结尾节点上。

    但是,就算这样,我们也无法非常便捷地求答案。

    考虑转换思路,以(y)串为枚举对象,查询有多少节点在(x)串的结尾节点的子树内。

    查询子树内信息,我们有经典结构dfs序

    我们可以将询问离线下来,并将询问存储在(y)节点的位置上。然后在trie树上爆搜(注意是trie树,不是trie图,在求(fail)时连上的新点不能进入,不然会挂),每到一个节点就统计(以当前节点到根所表示的字符串)作为(y)的答案。

    在dfs序上单点修改,区间求和,我们有树状数组

    关键代码(爆炸警告):

    for(int j=0;j<v[t[x].fin[i]].size();j++)
    	res[v[t[x].fin[i]][j].second]=
        	ask(t[pos[v[t[x].fin[i]][j].first]].dfn+t[pos[v[t[x].fin[i]][j].first]].sz-1)-
            ask(t[pos[v[t[x].fin[i]][j].first]].dfn-1);
    

    (v)是一个(vector),存的是询问,其中(first)(x)串,(second)是序号。

    (res)是统计答案的数组。

    (ask)是树状数组的前缀和。

    (dfn)是dfs序。

    (sz)(fail)树中子树大小。

    注意哪里是(fail)树,哪里是trie树,哪里是trie图!!

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,S,cnt=1,head[100100],lim,pos[100100],BIT[100100],res[100100];
    char s[100100];
    struct Edge{
    	int to,next;
    }edge[200100];
    void ae(int u,int v){
    	static int tot;
    	edge[tot].next=head[u],edge[tot].to=v,head[u]=tot++;
    }
    void add(int x,int val){
    	while(x<=lim)BIT[x]+=val,x+=x&-x;
    }
    int ask(int x){
    	int sum=0;
    	while(x)sum+=BIT[x],x-=x&-x;
    	return sum;
    }
    struct AC_automaton{
    	int ch[26],fa,fail,dfn,sz;
    	bool org[26];
    	vector<int>fin;
    }t[100100];
    void ins(){
    	int x=1;
    	for(int i=0;i<S;i++){
    		if(s[i]=='P')t[x].fin.push_back(++n),pos[n]=x;
    		else if(s[i]=='B'){
    			if(x==1)continue;
    			x=t[x].fa;
    		}else{
    			if(!t[x].ch[s[i]-'a'])t[x].ch[s[i]-'a']=++cnt,t[cnt].fa=x,t[x].org[s[i]-'a']=true;
    			x=t[x].ch[s[i]-'a'];
    		}
    	}
    }
    vector<pair<int,int> >v[100100];
    queue<int>q;
    void build(){
    	for(int i=0;i<26;i++){
    		if(!t[1].ch[i])t[1].ch[i]=1;
    		else t[t[1].ch[i]].fail=1,q.push(t[1].ch[i]),ae(1,t[1].ch[i]);
    	}
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		for(int i=0;i<26;i++){
    			if(t[x].ch[i])t[t[x].ch[i]].fail=t[t[x].fail].ch[i],q.push(t[x].ch[i]),ae(t[t[x].fail].ch[i],t[x].ch[i]);
    			else t[x].ch[i]=t[t[x].fail].ch[i];
    		}
    	}
    }
    void getdfn(int x){
    //	printf("%d
    ",x);
    	t[x].dfn=++lim,t[x].sz=1;
    	for(int i=head[x];i!=-1;i=edge[i].next)getdfn(edge[i].to),t[x].sz+=t[edge[i].to].sz;
    //	printf("%d
    ",x);
    }
    void dfs(int x){
    	add(t[x].dfn,1);
    	for(int i=0;i<t[x].fin.size();i++){
    //		printf("%d:%d
    ",x,t[x].fin[i]);
    		for(int j=0;j<v[t[x].fin[i]].size();j++)res[v[t[x].fin[i]][j].second]=ask(t[pos[v[t[x].fin[i]][j].first]].dfn+t[pos[v[t[x].fin[i]][j].first]].sz-1)-ask(t[pos[v[t[x].fin[i]][j].first]].dfn-1);
    	}
    	for(int i=0;i<26;i++)if(t[x].org[i])dfs(t[x].ch[i]);
    	add(t[x].dfn,-1);
    }
    int main(){
    	scanf("%s",s),S=strlen(s),memset(head,-1,sizeof(head)),ins(),build(),getdfn(1);
    	scanf("%d",&m);
    	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),v[y].push_back(make_pair(x,i));
    	dfs(1);
    	for(int i=1;i<=m;i++)printf("%d
    ",res[i]);
    	return 0;
    }
    
  • 相关阅读:
    mapreduce 的过程
    bootstrap当中,实现一些常用的元素居中
    如何理解人工智能、机器学习和深度学习三者的关系
    MapReduce的局限性
    MapReduce的计算资源划分
    Java中的堆和栈的区别
    java面试01-网络知识
    01Java经典问题
    06数据库复习03
    05数据库复习02
  • 原文地址:https://www.cnblogs.com/Troverld/p/12781116.html
Copyright © 2011-2022 走看看