【Codeforces710F】String Set Queries (强制在线)AC自动机

这个强制在线AC自动机。。。。
AC自动机是不可以修改的。
但是可以做到重构,合并 O ( n ) O(n) O(n)
那么就开两个AC自动机, s i z e size size分别为 n , m ( m &lt; n ) n,m(m&lt;n) n,mm<n
那么每次 m &gt; S m&gt;S m>S时就将自动机合并,每次往第二个自动机加串时就重构。
那么合并 O ( n S ) O(\frac nS) O(Sn)次,重构 O ( n ) O(n) O(n)次,重构的自动机大小为 O ( S ) O(S) O(S)
复杂度 O ( n S ∗ n + n S ) &gt; = O ( n 3 2 ) , S = n O(\frac nS * n + nS) &gt;= O(n^{\frac 32}) , S = \sqrt n O(Snn+nS)>=O(n23),S=n 时取。
但是这并没有结束。
为什么一定要用两个自动机呢?

考虑没有删除的情况,将插入串的个数二进制拆分,对于每 2 k 2^k 2k 个串维护一个ac自动机。
这样共有 log ⁡ n \log n logn a c ac ac自动机
。插入时如果当前 a c ac ac自动机中串的个数和上一个个数相同,那么合并两个自动机。每个字符串只会被合并 log ⁡ n \log n logn 次。
(但似乎时间差距并不是特别大,常数????)

O ( n n ) O(n\sqrt n) O(nn ) Code:

#include<bits/stdc++.h>
#define maxn 300005
#define Maxc 26
#define S 500
using namespace std;

struct AC
{
	int tr[maxn][Maxc],rtr[maxn][Maxc],fail[maxn],rtag[maxn],tag[maxn],tot;
	void clear()
	{
		for(int i=0;i<=tot;i++) memset(tr[i],0,sizeof tr[i]),memset(rtr[i],0,sizeof rtr[i]),
			fail[i]=rtag[i]=tag[i]=0;
		tot=0;
	}
	inline void Insert(char *s,int val,int u=0)
	{	for(int i=0,len=strlen(s),v;i<len;i++) v=s[i]-'a',u=rtr[u][v]?rtr[u][v]:rtr[u][v]=++tot;
		rtag[u]+=val;}
	void Build()
	{	queue<int>q;
		for(int i=0;i<=tot;i++) tag[i]=fail[i]=0,memset(tr[i],0,sizeof tr[i]);
		for(int i=0;i<Maxc;i++) if(rtr[0][i]) fail[tr[0][i]=rtr[0][i]]=0,q.push(tr[0][i]),tag[tr[0][i]]=rtag[tr[0][i]];
		for(int u=0;!q.empty();)
		{	u=q.front(),q.pop();
			for(int i=0;i<Maxc;i++) 
				if(rtr[u][i]) fail[tr[u][i]=rtr[u][i]]=tr[fail[u]][i],
					q.push(tr[u][i]),tag[tr[u][i]]=tag[fail[tr[u][i]]]+rtag[tr[u][i]];
				else tr[u][i]=tr[fail[u]][i];
		}
	}
	void Merge(const AC &B,int x,int y)
	{
		rtag[x]+=B.rtag[y];
		for(int i=0;i<Maxc;i++)
			if(B.rtr[y][i])
				Merge(B,rtr[x][i]?rtr[x][i]:rtr[x][i]=++tot,B.rtr[y][i]);
	}
	int Query(char *s,int u=0)
	{
		int ret = 0;
		for(int i=0,len=strlen(s);i<len;i++)
			u=tr[u][s[i]-'a'],ret+=tag[u];
		return ret;
	}
}A,B;
char s[maxn];
int main()
{
	/*
	freopen("1.in","r",stdin);
	freopen("2.out","w",stdout);
	*/
	int m,op;
	for(scanf("%d",&m);m--;)
	{	scanf("%d",&op);
		scanf("%s",s);
		int n=strlen(s);
		if(op==1)
		{
			if(n < S) B.Insert(s,1),B.Build();
			else A.Insert(s,1),A.Build();
			if(B.tot >= S)
			{
				A.Merge(B,0,0),A.Build();
				B.clear();
			}
		}
		else if(op==2)
		{
			if(n < S) B.Insert(s,-1),B.Build();
			else A.Insert(s,-1),A.Build();
			if(B.tot >= S)
			{
				A.Merge(B,0,0),A.Build();
				B.clear();
			}
		}
		else 
		{
			printf("%d\n",A.Query(s)+B.Query(s));
			fflush(stdout);
		}
	}
}

O ( n log ⁡ n ) O(n\log n) O(nlogn)Code(比 O ( n n ) O(n\sqrt n) O(nn )短??)

#include<bits/stdc++.h>
#define maxn 300005
#define Maxc 26
using namespace std;

struct AC
{
	int tr[maxn][Maxc],rtr[maxn][Maxc],fail[maxn],rtag[maxn],tag[maxn];
	int tot;
	inline void Insert(char *s,int val,int u=0)
	{	for(int i=0,len=strlen(s),v;i<len;i++) v=s[i]-'a',u=rtr[u][v]?rtr[u][v]:rtr[u][v]=++tot;
		rtag[u]+=val;}
	void Build(int u=0)
	{	queue<int>q; 
		fail[u]=u;
		for(int i=0;i<Maxc;i++) 
			if(rtr[u][i]) fail[tr[u][i]=rtr[u][i]]=u,q.push(tr[u][i]),tag[tr[u][i]]=rtag[tr[u][i]];
			else tr[u][i]=u;
		for(;!q.empty();)
		{	u=q.front(),q.pop();
			for(int i=0;i<Maxc;i++) 
				if(rtr[u][i]) fail[tr[u][i]=rtr[u][i]]=tr[fail[u]][i],
					q.push(tr[u][i]),tag[tr[u][i]]=tag[fail[tr[u][i]]]+rtag[tr[u][i]];
				else tr[u][i]=tr[fail[u]][i];
		}
	}
	void Merge(int &x,int &y)
	{
		if(!x||!y) x=x+y;
		else 
		{	
			rtag[x] += rtag[y];
			for(int i=0;i<Maxc;i++)
				if(rtr[y][i])
					Merge(rtr[x][i],rtr[y][i]);
		}
	}
	int Query(char *s,int u=0)
	{
		int ret = 0;
		for(int i=0,len=strlen(s);i<len;i++)
			u=tr[u][s[i]-'a'],ret+=tag[u];
		return ret;
	}
}A;
int tl=0,siz[30],rt[30];

void Insert(char *s,int val)
{	if(tl && siz[tl-1] == 1) A.Insert(s,val,rt[tl-1]),siz[tl-1]++;
	else A.Insert(s,val,rt[tl]=++A.tot),tl++,siz[tl-1]=1;
	for(;tl>1 && siz[tl-1] == siz[tl-2];tl--) A.Merge(rt[tl-2],rt[tl-1]),rt[tl-1]=0,siz[tl-2]+=siz[tl-1],siz[tl-1]=0;
	A.Build(rt[tl-1]);}

char s[maxn];
int main()
{
	/*
	freopen("1.in","r",stdin);
	freopen("2.out","w",stdout);
	*/
	int m,op;
	for(scanf("%d",&m);m--;)
	{	scanf("%d",&op);
		scanf("%s",s);
		int n=strlen(s);
		if(op==1) Insert(s,1);
		else if(op==2) Insert(s,-1);
		else 
		{
			int ans = 0;
			for(int i=0;i<tl;i++) ans += A.Query(s,rt[i]);
			printf("%d\n",ans);
			fflush(stdout);
		}
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值