• CF710F String Set Queries


    CF710F String Set Queries

    题意

    动态支持加入删除字符串和字符串匹配

    思路

    动态 AC自动机

    先不考虑动态情况。对于加入一个字符串,直接插入到自动机即可。

    考虑删除。发现对于答案具有可减性,意思是对于同一个匹配串,答案可以表示为在自动机中匹配的答案减去删去的所有串匹配的答案。那我们考虑给所有删除的串也开一个 AC 自动机,每次询问在两个自动机中匹配将答案相减即可。

    现在考虑动态情况。因为强制在线,且 AC 自动机是离线的,我们每插入一个字符串相当于重新建了一个自动机,每次在所有自动机中匹配结果的和即为答案。减少时间消耗的唯一途径是对 AC 自动机进行合并。

    考虑如何合并时间复杂度有保证且优秀。有很多办法,比如根号重构、二进制分组等。后者比较优秀也比较好写。

    实现

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cctype>
    #include<cstring>
    #include<cmath>
    #include<string>
    using namespace std;
    namespace star
    {
    	const int maxn=3e5+10;
    	struct ACmation{
    		int tot,son[maxn][26],val[maxn],fail[maxn],st[maxn],N,n,siz[maxn],beg[maxn];
    		string S[maxn];
    		inline void ins(string s,int ro){
    			int x=ro;
    			for(auto t:s){
    				t-='a';
    				if(son[x][t]==ro){
    					son[x][t]=++tot;
    					for(int j=0;j<26;j++) son[tot][j]=ro;
    				}
    				x=son[x][t];
    			}
    			val[x]++;
    		}
    		inline void build(int ro){
    			fail[ro]=ro;
    			static int q[maxn];
    			int hd=0,tl=1;
    			q[tl]=ro;
    			while(hd<tl){
    				int x=q[++hd];
    				for(int t=0;t<26;t++){
    					int u=son[x][t];
    					if(u!=ro){
    						fail[u]=x==ro?x:son[fail[x]][t];
    						if(son[fail[x]][t]!=u) val[u]+=val[son[fail[x]][t]];
    						q[++tl]=u;
    					}else son[x][t]=son[fail[x]][t];
    				}
    			}
    		}
    		inline int query(string s,int x){
    			int ans=0;
    			for(auto t:s)
    				x=son[x][t-'a'],ans+=val[x];
    			return ans;
    		}
    		inline void merge(){
    			siz[--N]<<=1;
    			for(int i=st[N];i<=tot;i++){
    				val[i]=fail[i]=0;
    				for(int j=0;j<26;j++) son[i][j]=0;
    			}
    			tot=st[N];
    			for(int i=0;i<26;i++) son[tot][i]=tot;
    			for(int i=beg[N];i<=n;i++) ins(S[i],st[N]);
    			build(st[N]);
    		}
    		inline void insert(string s){
    			st[++N]=++tot;
    			for(int i=0;i<26;i++) son[tot][i]=tot;
    			siz[N]=1;
    			ins(s,tot),build(st[N]);
    			S[beg[N]=++n]=s;
    			while(siz[N]==siz[N-1]) merge();
    		}
    		inline int query(string s){
    			int ans=0;
    			for(int i=1;i<=N;i++) ans+=query(s,st[i]);
    			return ans;
    		}
    	}a,b;
    	inline void work(){
    		int m;cin>>m;
    		while(m--){
    			int op;string s;
    			cin>>op>>s;
    			switch(op){
    				case 1:a.insert(s);break;
    				case 2:b.insert(s);break;
    				case 3:cout<<a.query(s)-b.query(s)<<endl;break;
    			}
    		}
    	}
    }
    signed main(){
    	star::work();
    	return 0;
    }
    
  • 相关阅读:
    用Vue创建一个新的项目
    事件循环学习2
    事件循环学习笔记
    关于访问器属性
    bootstrap-datetimepicker时间控件
    前端的指导方针---css篇
    web移动端小tip,box-flex
    数组常用的几种方法
    ajax对一些没有接口的数据进行分析和添加方法
    JAVA静态代理和动态代理理解
  • 原文地址:https://www.cnblogs.com/BrotherHood/p/14324410.html
Copyright © 2020-2023  润新知