题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2555
题目分析:多串匹配单串,强制在线,只在末端插入字符,让我们很容易想到SAM。其实如果做过阿狸的打字机这题的话,再看此题就显得很简单了。正如我之前所说,SAM本质上是将一个串的所有后缀做成AC自动机,并对其状态进行了化简,使其达到线性的时间和空间复杂度。我们将原串的后缀自动机建出来,查询的时候沿着SAM跑,所到达的状态的Right集的大小便是答案。因为所有的r属于Right都逐一对应匹配串在原串的一个结尾位置。要维护Right集的大小,我们可以考虑在每一次查询的时候,将该状态在parent树上所代表的区间Splay出来(注意拆点),然后查看里面叶子节点的个数;或者考虑逆向思维,在parent树上新插入一个叶子节点的时候,将它到根路径上的Right_Size全部+1,查询时O(1)得到答案。
这题还有一个坑点,就是解密的时候全局变量mask的值是不变的,只有Query的时候才会变,我一开始看题的时候很奇怪:mask不是函数里面的局部变量吗,为什么下面又说mask^=Result?交了几次死活WA,看了看网上PoPoQQQ大神的代码,才发现这两个mask不是同一个变量……
CODE(Splay+DFS序):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1200010;
const int maxm=30000010;
const int maxc=26;
typedef long long LL;
struct Tnode
{
int num,Size;
Tnode *fa,*son[2];
int Get_d() { return fa->son[1]==this; }
void Connect(Tnode *P,int d) { (son[d]=P)->fa=this; }
void Up() { Size=num+(son[0]? son[0]->Size:0)+(son[1]? son[1]->Size:0); }
} tree[maxn<<1];
Tnode *Troot;
int Tcur=-1;
struct State
{
int R;
Tnode *st,*ed;
State *son[maxc],*parent;
} SAM[maxn];
State *Sroot,*last;
int Scur=-1;
LL mask=0;
char op[maxc];
char s[maxm];
int n,len=1;
Tnode *New_node(int val)
{
Tcur++;
tree[Tcur].num=tree[Tcur].Size=val;
tree[Tcur].fa=tree[Tcur].son[0]=tree[Tcur].son[1]=NULL;
return tree+Tcur;
}
State *New_state(int val,int x)
{
Scur++;
SAM[Scur].R=val;
SAM[Scur].st=New_node(x);
SAM[Scur].ed=New_node(0);
SAM[Scur].st->Connect(SAM[Scur].ed,1);
for (int i=0; i<maxc; i++) SAM[Scur].son[i]=NULL;
SAM[Scur].parent=NULL;
return SAM+Scur;
}
void Zig(Tnode *P,Tnode *&tag)
{
int d=P->Get_d();
Tnode *F=P->fa;
if (P->son[!d]) F->Connect(P->son[!d],d);
else F->son[d]=NULL;
if (F!=tag) F->fa->Connect(P, F->Get_d() );
else P->fa=F->fa,tag=P;
P->Connect(F,!d);
F->Up();
}
void Splay(Tnode *P,Tnode *&tag)
{
Tnode *F;
while (P!=tag)
{
F=P->fa;
if (F!=tag) ( F->Get_d()^P->Get_d() )? Zig(P,tag):Zig(F,tag);
Zig(P,tag);
}
P->Up();
}
Tnode *Find_succ(Tnode *P)
{
if (P->son[0]) return Find_succ(P->son[0]);
return P;
}
Tnode *Find_root(Tnode *P)
{
if (P->fa) return Find_root(P->fa);
return P;
}
void Add(State *x,State *y)
{
Splay(x->st,Troot);
Tnode *succ=Find_succ(Troot->son[1]);
Splay(succ,Troot->son[1]);
Tnode *yRoot=Find_root(y->st);
Splay(y->st,yRoot);
Troot->son[1]->Connect(yRoot,0);
Troot->son[1]->Up();
Troot->Up();
y->parent=x;
}
Tnode *Find_prev(Tnode *P)
{
if (P->son[1]) return Find_prev(P->son[1]);
return P;
}
void Spilit(State *x)
{
Splay(x->st,Troot);
Tnode *prev=Find_prev(Troot->son[0]);
Splay(x->ed,Troot);
Tnode *succ=Find_succ(Troot->son[1]);
Splay(prev,Troot);
Splay(succ,Troot->son[1]);
Troot->son[1]->son[0]->fa=NULL;
Troot->son[1]->son[0]=NULL;
Troot->son[1]->Up();
Troot->Up();
}
void Build(int x)
{
int to=s[x]-'A';
x+=len;
State *NP=New_state(x,1),*P=last;
last=NP;
while ( P && !P->son[to] ) P->son[to]=NP,P=P->parent;
if (!P) Add(Sroot,NP);
else
{
State *Q=P->son[to];
if (P->R+1==Q->R) Add(Q,NP);
else
{
State *NQ=New_state(P->R+1,0);
for (int i=0; i<maxc; i++) NQ->son[i]=Q->son[i];
Add(Q->parent,NQ);
Spilit(Q);
Add(NQ,Q);
Add(NQ,NP);
while ( P && P->son[to]==Q ) P->son[to]=NQ,P=P->parent;
}
}
}
void Change(int x)
{
LL slen=strlen(s);
for (LL i=0; i<slen; i++)
{
x=(x*131LL+i)%slen;
swap(s[i],s[x]);
}
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d",&n);
scanf("%s",&s);
last=Sroot=New_state(0,0);
Troot=Sroot->st;
int slen=strlen(s);
for (int i=0; i<slen; i++) Build(i);
len+=slen;
while (n--)
{
scanf("%s %s",&op,&s);
Change(mask);
int slen=strlen(s);
if (op[0]=='A')
{
for (int i=0; i<slen; i++) Build(i);
len+=slen;
}
else
{
State *P=Sroot;
for (int i=0; i<slen; i++)
{
P=P->son[ s[i]-'A' ];
if (!P) break;
}
if (!P) printf("0\n");
else
{
Splay(P->st,Troot);
Splay(P->ed,Troot->son[1]);
Tnode *tag=Troot->son[1]->son[0];
int ans=0;
if (tag) ans=tag->Size;
ans+=P->st->num;
printf("%d\n",ans);
mask^=(long long)ans;
}
}
}
return 0;
}