• 题解 CF1207G Indie Album


    (Large atural) CF1207G Indie Album 原题链接

    解法

    这种题算是比较套路的了,我们看见这种多字符串匹配,会想到AC自动机

    如果不会AC自动机的可以看看 我的博客 ,希望能给您带来帮助。

    那么如果我们对于所有字典树节点连边 (i-fail_i)(根节点没有 (fail),不连边),那么就得到了一棵 Fail树

    那么,如果一号点为根节点,那所有 (fail) 直接或间接指向 (i) 号点的节点,都在 (i) 的子树中。

    所以查询字符串 (X) 在字符串 (Y) 中出现几次,等价于建出 TrieFail树 后,在 Fail树中 以 “(X)的结束节点”(设为 (i)) 为根的子树中有多少个 (Y) 包含的节点。

    不理解可以看这解释:比如 (j) 号点是 (Y) 所包含的,是 (Y) 的第 (id) 个节点,那么代表在 (Y) 查询时,(j)(fail) 可以跑出 (X),所以 (X)(Y)(1sim id) 这个子串的后缀。

    对于这道题,我们可以类比 阿机的打字狸

    设询问的字符串为 (T),则显然有这样一个方法:让 (T)Trie 上跑,经过的点对于的 Fail树节点权值加一。然后我们查询 (S)Trie 的结尾节点 (G),对应的 Fail树节点 ,的子树和(可能有点绕)。最后我们再把那些加一的点再减一,复原原来的树。

    把答案离线后,我们可以在 Trie树DFS,来到一个点,这个点对于的 Fail树节点 权值加一;离开一个点,同样地权值减一。这样当我们到达某个字符串的末尾时,我们可以保证有且仅有这个字符串的点上有值。然后我们调出在这个点上的每一个询问,查询它们对应的 Fail树节点 的子树和即可。

    所以我们需要支持单点加值,以及查询一个点的子树和,可以用 DFS序 解决。

    因为一个点的子树在 DFS序 上是连续的,所以我们只用区间查询(给子树查询)、单点加值( 在 Trie 跑上 DFS 时加减)就行了,显然套树状数组模板即可。

    代码

    #include<bits/stdc++.h>
    #define rep(i,x,y) for(int i=x;i<=y;++i)
    #define mar(o) for(int E=fst[o];E;E=e[E].nxt)
    #define v e[E].to
    using namespace std;
    const int n7=401234,m7=801234,z7=1601234;
    struct dino{int to,nxt;}e[z7];
    struct lyca{int z,id;};
    int n,T,cnt=1,tre[m7][26],fail[m7],poi[n7],head,tail,que[m7];
    int ecnt,fst[m7],t,atre[n7],L[n7],R[n7],ans[n7];
    char cr[n7];bool tru[m7][26];
    vector <lyca> vec[m7];
    
    int rd(){
       int shu=0;char ch=getchar();
       while(!isdigit(ch))ch=getchar();
       while(isdigit(ch))shu=(shu<<1)+(shu<<3)+(ch^48),ch=getchar();
       return shu;
    }
    
    void edge(int sta,int edn){
    	ecnt++;
    	e[ecnt]=(dino){edn,fst[sta]};
    	fst[sta]=ecnt;
    }
    
    void insert1(){
    	rep(i,1,n){
    		int sys=rd(),las=1;
    		if(sys==2)las=poi[ rd() ];
    		char ch=getchar()-'a';
    		if(!tre[las][ch]){
    			cnt++,tre[las][ch]=cnt;
    		}
    		poi[i]=tre[las][ch];
    	}
    }
    
    void insert2(int z){
    	int len=strlen(cr+1),now=1;
    	rep(i,1,len){
    		int ch=cr[i]-'a';
    		if(!tre[now][ch]){
    			cnt++,tre[now][ch]=cnt;
    		}
    		now=tre[now][ch];
    	}
    	poi[z]=now;
    }
    
    void Gfail(){
    	head=1,tail=1,que[1]=1;
    	rep(i,0,25)tre[0][i]=1;
    	while(head<=tail){
    		int now=que[head];
    		rep(i,0,25){
    			int edn=tre[ fail[now] ][i];
    			if(tre[now][i]){
    				fail[ tre[now][i] ]=edn;
    				edge(edn,tre[now][i]);
    				tail++,que[tail]=tre[now][i];
    				tru[now][i]=1;
    			}
    			else tre[now][i]=edn;
    		}
    		head++;
    	}
    }
    
    #define lb(z) (z&-z)
    void updat(int z,int id){
    	while(id<=cnt)atre[id]+=z,id+=lb(id);
    }
    int Dquery(int id){
    	int tot=0;
    	while(id)tot+=atre[id],id-=lb(id);
    	return tot;
    }
    int query(int l,int r){
    	return Dquery(r)-Dquery(l-1);
    }
    
    void dfs1(int o){
    	t++,L[o]=t;
    	mar(o)dfs1(v);
    	R[o]=t;
    }
    
    int fimd(){
    	int len=strlen(cr+1),now=1;
    	rep(i,1,len)now=tre[now][ cr[i]-'a' ];
    	return now;
    }
    
    void dfs2(int o){
    	updat(1,L[o]);
    	int wal=vec[o].size()-1;
    	rep(i,0,wal){
    		int ll=L[ vec[o][i].z ];
    		int rr=R[ vec[o][i].z ];
    		ans[ vec[o][i].id ]=query(ll,rr);
    	}
    	rep(i,0,25){
    		if(tru[o][i])dfs2(tre[o][i]); 
    	}
    	updat(-1,L[o]);
    }
    
    int main(){
    	n=rd(),insert1(),T=rd();
    	rep(i,1,T){
    		int z=rd();scanf("%s",cr+1);
    		insert2(i+n);
    		vec[ poi[z] ].push_back( (lyca){poi[i+n],i} );
    	}
    	Gfail(),dfs1(1),dfs2(1);
    	rep(i,1,T)printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Zookeeper入门
    Redis五种数据类型
    shardingJDBC分库分表
    RabbitMQ入门
    Spring-Boot
    spring-mvc
    oracle一些对表的操作命令
    对IFeatureClass的选择结果进行再次选择及排序
    关于基础 DBMS 错误 ORA-01654的总结
    Linq 如何实现 in 与 not in
  • 原文地址:https://www.cnblogs.com/BlankAo/p/14375202.html
Copyright © 2020-2023  润新知