题意:有n个操作,一个是学习一个串,另一个一个串中有多少子串是学习过的。
思路:利用AC自动机可以很容易实现第二个操作,但是AC自动机是不能在线修改的,每次添加一个串以后就需要重构自动机,这样显然复杂度非常高。为了降低复杂度,可以建立两个自动机,先在第二个自动机中添加串,每次添加完后进行重构,当第二个串中的字符总数超过sqrt(n)时,将第二个自动机中的串添加到第一个自动机中,并进行重构。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<bitset>
#include<set>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-6
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+100;
const int maxm=5000000+10;
const int Size=2333;
char str[maxn],pargraph[maxm],stmp[maxm];
int S[maxn],T[maxn];
struct AC
{
int ch[maxn*2][2],val[maxn*2],next[maxn*2],lastv[maxn*2];
int size;
void Init()
{
ch[0][0]=ch[0][1]=0;
val[0]=size=0;
}
void Insert(const char * s,int L,int R)
{
int u=0,c;
for(int i=L;i<=R;++i)
{
c=s[i]-'0';
if(!ch[u][c])
{
ch[u][c]=++size;
ch[size][0]=ch[size][1]=0;
val[size]=0;
}
u=ch[u][c];
}
val[u]=1;
}
bool check(const char * s,int L,int R)
{
int u=0,c;
for(int i=L;i<=R;++i)
{
c=s[i]-'0';
if(!ch[u][c]) return false;
u=ch[u][c];
}
return val[u];
}
void build()
{
queue<int>q;
memset(next,0,sizeof(int)*(size+2));
memset(lastv,0,sizeof(int)*(size+2));
if(ch[0][0]) q.push(ch[0][0]);
if(ch[0][1]) q.push(ch[0][1]);
int u,v;
while(!q.empty())
{
u=q.front();q.pop();
for(int c=0;c<2;++c)
{
v=ch[u][c];
if(!v) { continue; }
q.push(v);
int j=next[u];
while(j&&!ch[j][c]) j=next[j];
next[v]=ch[j][c];
lastv[v]=val[next[v]]?next[v]:lastv[next[v]];
}
}
}
int Find(const char *s,int len)
{
int u=0,v,c;
int cnt=0;
for(int i=0;i<len;++i)
{
c=s[i]-'0';
while(u&&!ch[u][c]) u=next[u];
u=ch[u][c];
if(val[u]||lastv[u])
{
v=val[u]?u:lastv[u];
while(v)
{
cnt+=val[v];
v=lastv[v];
}
}
}
return cnt;
}
}BigAc,SmallAc;
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t,n,tcase=0;
scanf("%d",&t);
while(t--)
{
int L=0,tot=0,len,csize=0,pre=0;
S[0]=T[0]=0;
BigAc.Init();
SmallAc.Init();
scanf("%d",&n);
printf("Case #%d:\n",++tcase);
while(n--)
{
scanf("%s",stmp);
len=strlen(stmp);
int p=L%(len-1),m=0;
for(int i=p+1;i<len;++i)
pargraph[m++]=stmp[i];
for(int i=1;i<=p;++i)
pargraph[m++]=stmp[i];
if(stmp[0]=='+')
{
for(int i=1;i<len;++i)
str[T[tot]+i]=pargraph[i-1];
tot++;
S[tot]=T[tot-1]+1;
T[tot]=T[tot-1]+len-1;
if(BigAc.check(str,S[tot],T[tot])||SmallAc.check(str,S[tot],T[tot])) {tot--;continue;}
csize+=len-1;
if(csize>Size)
{
for(int j=pre+1;j<=tot;++j)
BigAc.Insert(str,S[j],T[j]);
BigAc.build();
SmallAc.Init();
pre=tot;csize=0;
}
else
{
SmallAc.Insert(str,S[tot],T[tot]);
SmallAc.build();
}
}
else
{
int ans=0;
ans+=BigAc.Find(pargraph,m);
ans+=SmallAc.Find(pargraph,m);
L=ans;
printf("%d\n",ans);
}
}
}
return 0;
}