zoukankan      html  css  js  c++  java
  • 【CF163E】e-Government

    题目

    题目链接:https://codeforces.com/problemset/problem/163/E
    给定包含 (n) 个字符串的集合 (S),有 (m) 个操作,操作有三种类型:

    • ? 开头的操作为询问操作,询问当前字符串集 (S) 中的每一个字符串匹配询问字符串的次数之和;
    • + 开头的操作为添加操作,表示将编号为 (i) 的字符串加入到集合中;
    • - 开头的操作为删除操作,表示将编号为 (i) 的字符串从集合中删除。

    注意当编号为 (i) 的字符串已经在集合中时,允许存在添加编号为 (i) 的字符串,删除亦然。
    (n,mleq 10^5,sum |s_i|leq 10^6)

    思路

    (n) 个字符串都扔进一个 AC 自动机里。我们知道计算 (t) 串在 (s) 串中的出现次数,可以在 AC 自动机上不断匹配 (S) 串,对于 (S) 串每一个后缀,找 fail 树上它的祖先是否有 (t) 串结尾所表示的点。
    那么如果添加了 (t) 串进集合,等价于让 (t) 串结尾所表示节点,在 fail 树上的子树全部多了一次匹配。
    那么只需要将 fail 树的 dfs 序求出来,然后树状数组维护区间加,单点查询即可。
    时间复杂度 (O(mlog (sum |S_i|)))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=100010,M=1000010;
    int n,m,lim,ed[N],id[M],siz[M];
    bool vis[N];
    char s[M];
    
    struct BIT
    {
    	ll c[M];
    	
    	void add(int x,ll v)
    	{
    		for (int i=x;i<=lim;i+=i&-i)
    			c[i]+=v;
    	}
    	
    	ll query(int x)
    	{
    		ll ans=0;
    		for (int i=x;i;i-=i&-i)
    			ans+=c[i];
    		return ans;
    	}
    }bit;
    
    struct ACA
    {
    	int tot,fa[M],fail[M],ch[M][26];
    	vector<int> e[M];
    	
    	void insert(char *s,int j,int st=1)
    	{
    		int p=0,len=strlen(s+1);
    		for (int i=st;i<=len;i++)
    		{
    			if (!ch[p][s[i]-'a'])
    				ch[p][s[i]-'a']=++tot,fa[tot]=p;
    			p=ch[p][s[i]-'a'];
    		}
    		ed[j]=p;
    	}
    	
    	void build()
    	{
    		queue<int> q;
    		for (int i=0;i<26;i++)
    			if (ch[0][i]) q.push(ch[0][i]);
    		while (q.size())
    		{
    			int u=q.front(); q.pop();
    			e[fail[u]].push_back(u);
    			for (int i=0;i<26;i++)
    				if (ch[u][i]) fail[ch[u][i]]=ch[fail[u]][i],q.push(ch[u][i]);
    					else ch[u][i]=ch[fail[u]][i];
    		}
    	}
    	
    	void dfs(int x)
    	{
    		id[x]=++tot; siz[x]=1;
    		for (int i=0;i<e[x].size();i++)
    		{
    			int v=e[x][i];
    			dfs(v); siz[x]+=siz[v];
    		}
    	}
    	
    	void query(char *s)
    	{
    		int len=strlen(s+1),p=0;
    		ll ans=0;
    		for (int i=2;i<=len;i++)
    		{
    			p=ch[p][s[i]-'a'];
    			ans+=bit.query(id[p]);
    		}
    		cout<<ans<<"
    ";
    	}
    }AC;
    
    int main()
    {
    	scanf("%d%d",&m,&n);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%s",s+1);
    		AC.insert(s,i);
    	}
    	AC.build();
    	AC.tot=0; AC.dfs(0);
    	lim=AC.tot;
    	for (int i=1;i<=n;i++)
    	{
    		vis[i]=1;
    		bit.add(id[ed[i]],1);
    		bit.add(id[ed[i]]+siz[ed[i]],-1);
    	}
    	for (int i=1;i<=m;i++)
    	{
    		scanf("%s",s+1);
    		int len=strlen(s+1);
    		if (s[1]=='?') AC.query(s);
    		if (s[1]=='+')
    		{
    			int x=0;
    			for (int j=2;j<=len;j++)
    				x=x*10+s[j]-48;
    			if (!vis[x])
    			{
    				vis[x]=1;
    				bit.add(id[ed[x]],1);
    				bit.add(id[ed[x]]+siz[ed[x]],-1);
    			}
    		}
    		if (s[1]=='-')
    		{
    			int x=0;
    			for (int j=2;j<=len;j++)
    				x=x*10+s[j]-48;
    			if (vis[x])
    			{
    				vis[x]=0;
    				bit.add(id[ed[x]],-1);
    				bit.add(id[ed[x]]+siz[ed[x]],1);
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux-netstat
    API接口防止参数篡改和重放攻击
    Java中遍历Map的几种方式
    Java泛型中的标记符含义
    Iterator 和 for...of 循环
    Promise 对象
    Reflect
    正则要求密码长度最少12位,包含至少1个特殊字符,2个数字,2个大写字母和一些小写字母。
    一个JS正则表达式,一个正实数,整数部分最多11位 小数部分最多 8位
    java阿里云短信发送配置
  • 原文地址:https://www.cnblogs.com/stoorz/p/15036338.html
Copyright © 2011-2022 走看看