这个强制在线AC自动机。。。。
AC自动机是不可以修改的。
但是可以做到重构,合并
O
(
n
)
O(n)
O(n)
那么就开两个AC自动机,
s
i
z
e
size
size分别为
n
,
m
(
m
<
n
)
n,m(m<n)
n,m(m<n)
那么每次
m
>
S
m>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
)
>
=
O
(
n
3
2
)
,
S
=
n
O(\frac nS * n + nS) >= O(n^{\frac 32}) , S = \sqrt n
O(Sn∗n+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);
}
}
}