题意
要求你在线支持下面几个操作:
1.在当前字符串末尾插入一个字符串
2.查询一个字符串作为子串在当前字符串中出现了多少次
题解
后缀自动机
显然出现次数就是对应节点right大小对吧
那么动态维护怎么做呢?
LCT
再详细的讲讲怎么用LCT维护fail树(好像也叫parent树来着)吧(此处注意LCT并不完全是正常的LCT)
简单的讲,就是正常的SAM新建节点时在LCT新建节点,更新后缀链接时同时在LCTcut原来的边并link新边
而怎么进行更新right大小呢
我们储存两个信息,val和tag,val就用来保存答案,tag是懒标记
每次加上新编边时,我们Access fa,Splay fa,其实就差个翻转就跟makeroot一样了
但是注意,fail树的形态是固定的,也就是说,我们不能翻转,然后我们把这个节点的贡献上传,打上懒标记
所以这个tag的作用就很明显了,其实就是用来更新这个更改点到根的路径上的点的val
cut的时候也是类似的,减去对应的贡献
一堆细节
样例爆水…建议自己先测点
2
AB
ADD BBABB
QUERY BB
ans:3
那个mask是形参不是引用…
找对应节点时如果已经没有了转移,直接返回0(不要走fail…)
不要像我一样傻傻的每次插入写了个last=root
我震惊了…字符集开的2过了…
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
const int N=3000000+5,T=3000000+5;
const int INF=1<<30;
typedef long long ll;
char s[N];
struct node *NIL;
struct node{
int tag,val,sz;
node *fa,*ch[2];
int Dir(){
return this==fa->ch[1];
}
bool Isroot(){
return fa==NIL||(this!=fa->ch[0]&&this!=fa->ch[1]);
}
void Setchild(node *x,int d){
ch[d]=x;
if(x!=NIL)
x->fa=this;
}
void Pushup(){
sz=ch[0]->sz+ch[1]->sz+1;
}
void Pushdown(){
if(tag){
if(ch[0]!=NIL){
ch[0]->tag+=tag;
ch[0]->val+=tag;
}
if(ch[1]!=NIL){
ch[1]->tag+=tag;
ch[1]->val+=tag;
}
tag=0;
}
}
}tree[T],*tcnt;
void Debug(){
for(int i=1;tree+i<=tcnt;i++)
printf("%d lch:%d rch:%d fa:%d val:%d tag:%d\n",i,(tree+i)->ch[0]-tree,(tree+i)->ch[1]-tree,(tree+i)->fa-tree,(tree+i)->val,(tree+i)->tag);
}
void Init(){
tcnt=NIL=&tree[0];
NIL->fa=NIL->ch[0]=NIL->ch[1]=NIL;
}
node *Newnode(int val){
tcnt++;
tcnt->val=val,tcnt->sz=1;
tcnt->ch[0]=tcnt->ch[1]=tcnt->fa=NIL;
return tcnt;
}
void Rotate(node *x){
node *y=x->fa;
y->Pushdown();
x->Pushdown();
int d=x->Dir();
if(y->Isroot())
x->fa=y->fa;
else
y->fa->Setchild(x,y->Dir());
y->Setchild(x->ch[!d],d);
x->Setchild(y,!d);
y->Pushup();
}
void Splay(node *x){
x->Pushdown();
while(!x->Isroot()){
node *y=x->fa;
if(y->Isroot())
Rotate(x);
else{
if(x->Dir()^y->Dir())
Rotate(x);
else
Rotate(y);
Rotate(x);
}
}
x->Pushup();
}
void Access(node *x){
node *y=NIL;
while(x!=NIL){
Splay(x);
x->Setchild(y,1);
x->Pushup();
y=x;
x=x->fa;
}
}
void Link(node *x,node *y){
Access(y);
Splay(y);
x->fa=y;
y->tag+=x->val;
y->val+=x->val;
}
void Cut(node *x){
Access(x);
Splay(x);
x->ch[0]->tag-=x->val;
x->ch[0]->val-=x->val;
x->ch[0]->fa=NIL;
x->ch[0]=NIL;
}
class SAM{
public:
struct mode{
mode *fail;
mode *ch[26];
int len;
}a[T],*last,*tcnt,*root;
void Debug(){
for(int i=0;root+i<=tcnt;i++){
printf("%d:\n",i);
for(int j=0;j<26;j++)
if((root+i)->ch[j])
printf("%c->%d\n",'A'+j,(root+i)->ch[j]-root);
if(i)
printf("fa: %d\n",(root+i)->fail-root);
printf("len:%d\n",(root+i)->len);
}
}
node *z[T];
void Init(){
last=tcnt=root=&a[0];
z[0]=Newnode(0);
}
void Link(mode *p,mode *q){
::Link(z[p-root],z[q-root]);
//::Debug();
}
void Cut(mode *p){
::Cut(z[p-root]);
//::Debug();
}
void Insert(char *s){
int n=strlen(s);
//last=root;
for(int i=0;i<n;i++){
int c=s[i]-'A';
mode *p=last,*np=++tcnt,*q,*nq;
z[np-root]=Newnode(1);
last=np;
np->len=p->len+1;
while(p&&!p->ch[c])
p->ch[c]=np,p=p->fail;
if(!p){
np->fail=root;
Link(np,root);
}
else{
q=p->ch[c];
if(q->len==p->len+1){
np->fail=q;
Link(np,q);
}
else{
nq=++tcnt;
z[nq-root]=Newnode(0);
*nq=*q;
nq->len=p->len+1;
Link(nq,q->fail);
q->fail=nq;
np->fail=nq;
Cut(q);
Link(q,nq);
Link(np,nq);
while(p&&p->ch[c]==q)
p->ch[c]=nq,p=p->fail;
}
}
}
}
int Match(char *s){
int n=strlen(s);
mode *p=root;
for(int i=0;i<n;i++){
int c=s[i]-'A';
if(p->ch[c])
p=p->ch[c];
else
return 0;
}
Splay(z[p-root]);
return z[p-root]->val;
}
}sam;
int mask=0;
void decodeWithMask(int mask){
int n=strlen(s);
for(int j=0;j<n;j++){
mask=(mask*131+j)%n;
swap(s[j],s[mask]);
}
}
int main()
{
Init();
sam.Init();
int q;
scanf("%d",&q);
scanf("%s",s);
sam.Insert(s);
//Debug();
//sam.Debug();
for(int i=1;i<=q;i++){
scanf("%s",s);
if(strcmp(s,"ADD")==0){
scanf("%s",s);
decodeWithMask(mask);
sam.Insert(s);
//Debug();
//sam.Debug();
}
else{
scanf("%s",s);
decodeWithMask(mask);
int res=sam.Match(s);
mask^=res;
printf("%d\n",res);
}
}
}