【CF163E】e-Government

题目

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

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

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

思路

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

【CF163E】e-Government

上一篇:ES6中超级好用的模板字符串


下一篇:Update some properties