P5212 SubString
题目描述
给定一个字符串 init
,要求支持两个操作:
-
在当前字符串的后面插入一个字符串。
-
询问字符串 s s s 在当前字符串中出现了几次。(作为连续子串)
强制在线。
输入格式
第一行一个整数 Q Q Q 表示操作个数。
第二行一个字符串表示初始字符串 init
。
接下来
Q
Q
Q 行,每行
2
2
2 个字符串 Type
,Str
。
-
Type
是ADD
,表示在后面插入字符串。 -
Type
是QUERY
,表示询问某字符串在当前字符串中出现了几次。
为了体现在线操作,你需要维护一个变量 mask
,初始值为
0
0
0。
String decodeWithMask(String s, int mask) {
char[] chars = s.toCharArray();
for (int j = 0; j < chars.length; j++) {
mask = (mask * 131 + j) % chars.length;
char t = chars[j];
chars[j] = chars[mask];
chars[mask] = t;
}
return new String(chars);
}
读入串 Str
之后,使用这个过程将之解码成真正询问的串 TrueStr
。
询问的时候,对 TrueStr
询问后输出一行答案 Result
。
然后 m a s k = m a s k ⨁ R e s u l t mask=mask \bigoplus Result mask=mask⨁Result。
插入的时候,将TrueStr
插到当前字符串后面即可。
注意:ADD
和 QUERY
操作的字符串都需要解压。
输出格式
对于每一个 QUERY
操作,输出询问的字符串在当前字符串中出现了几次。
样例 #1
样例输入 #1
2
A
QUERY B
ADD BBABBBBAAB
样例输出 #1
0
如果不要求强制在线的话,把询问离线下来按照右端点排序,把插入字符串的过程改成删除字符串的过程,对于删除一个字符,就可以把这个字符对应的后缀树上的结点到根的链的
s
i
z
e
size
size全都减1,这个用树链剖分就可以很好的维护。
但是如果强制在线的话,就需要动态维护后缀树了。
这个时候单单使用树剖就行不通了,需要更为强大的数据结构
L
C
T
LCT
LCT来解决。
考虑如何用
L
C
T
LCT
LCT动态维护后缀树,这就需要分情况讨论:
在
S
A
M
SAM
SAM中插入结点的时候有两种情况,一种是在后缀树上插入一个点,一种是插入两个点,对于一个点的情况,我们只需要在
L
C
T
LCT
LCT上连接两个点即可,而对于两个点的情况,天上的结点需要继承儿子的权值,地上的结点直接连边就可,这样就可以动态维护后缀树了,对于每次插入的地上的结点,我们还需要把这个点到根上的路径
s
i
z
e
size
size都+1,这个可以通过
L
C
T
LCT
LCT的链修改操作很好的维护,只需要
s
p
l
i
t
split
split这个链,然后在
s
p
l
a
y
splay
splay树的根上打标记即可。不要忘记
p
u
s
h
d
o
w
n
pushdown
pushdown。
对于每一个询问,可以先通过
S
A
M
SAM
SAM的
D
A
G
DAG
DAG走到这个字符串的位置,然后这个点在后缀树上的
s
i
z
e
size
size就是我们要求的答案。
#include<bits/stdc++.h>
#define clean(x) memset(x,0,sizeof(x))
#define fil(x,n) fill(x,x+1+n,0)
#define inf 2000000009
#define maxn 3000005
// #define int long long
using namespace std;
int read()
{
int x=1,res=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
res=res*10+(c-'0');
c=getchar();
}
return res*x;
}
char a[maxn],b[maxn];
void Mask(char c[], int mask) {
for (int j=0;j<strlen(c);j++) {
mask=(mask*131+j)%strlen(c);
char t=c[j];
c[j]=c[mask];
c[mask]=t;
}
for(int j=0;j<=strlen(c);j++) b[j]=c[j];
}
struct edge{
int next,to;
};
struct LCT{
int top,ch[maxn][2],f[maxn],xr[maxn],q[maxn],rev[maxn],val[maxn];
long long sum[maxn];
int tag_mul[maxn],tag_add[maxn],sz[maxn],p[maxn];
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
void access(int x){for(int t=0;x;t=x,x=f[x])splay(x),ch[x][1]=t,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int find(int x){access(x);splay(x);while(ch[x][0])x=ch[x][0];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void link(int x,int y){makeroot(x);if(find(y)!=x)f[x]=y;}
void cut(int x,int y){
if(find(x)!=find(y)) return; split(x,y);
if(ch[y][0]==x&&ch[x][1]==0) ch[y][0]=0,f[x]=0;
if(ch[y][1]==x&&ch[x][0]==0) ch[y][1]=0,f[x]=0;
}
void pushup(int x){
// xr[x]=xr[ch[x][0]]^xr[ch[x][1]]^val[x];
sz[x]=(sz[ls(x)]+sz[rs(x)]+p[x]);
sum[x]=(val[x]+sum[ls(x)]+sum[rs(x)]);
}
void pushdown(int x){
int l=ch[x][0],r=ch[x][1];
if(rev[x]){
rev[l]^=1;rev[r]^=1;rev[x]^=1;
swap(ch[x][0],ch[x][1]);
}
if(tag_add[x]){
tag_add[ls(x)]+=tag_add[x];
val[ls(x)]+=tag_add[x];
sum[ls(x)]+=sz[ls(x)]*tag_add[x];
tag_add[rs(x)]+=tag_add[x];
val[rs(x)]+=tag_add[x];
sum[rs(x)]+=sz[rs(x)]*tag_add[x];
tag_add[x]=0;
}
}
void rotate(int x){
int y=f[x],z=f[y],l,r;
if(ch[y][0]==x)l=0;else l=1;r=l^1;
if(!isroot(y)){if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;}
f[x]=z;f[y]=x;f[ch[x][r]]=y;
ch[y][l]=ch[x][r];ch[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
top=1;q[top]=x;
for(int i=x;!isroot(i);i=f[i]) q[++top]=f[i];
for(int i=top;i;i--) pushdown(q[i]);
while(!isroot(x)){
int y=f[x],z=f[y];
if(!isroot(y)){
if((ch[y][0]==x)^(ch[z][0]==y)) rotate(x);
else rotate(y);
} rotate(x);
}
}
}lct;
struct SAM{
int id[maxn],pos[maxn],tot=1,lt=1,num,l[maxn],ch[maxn][26],sz[maxn],f[maxn],last[maxn];
edge g[maxn];
void init(int len){
for(int i=0;i<=len;i++){
id[i]=0;pos[i]=0;f[i]=0;pos[i]=0;
last[i]=0;l[i]=0;sz[i]=0;
for(int j=0;j<26;j++) ch[i][j]=0;
}
tot=lt=1;num=0;
}
void insert(int c,int i){
int v=++tot,u=lt;lt=tot;pos[tot]=i;id[i]=tot;
sz[v]=1;l[v]=l[u]+1;lct.p[v]=1;
while(u&&!ch[u][c]) {ch[u][c]=v;u=f[u];}
if(!u) {f[v]=1;lct.link(v,1);return;}
int x=ch[u][c];
if(l[x]==l[u]+1) {f[v]=x;lct.link(v,x);return;}
int y=++tot;pos[y]=pos[x];
lct.split(x,x);
lct.val[y]=lct.val[x];lct.sum[y]=lct.sum[x];lct.sz[y]=lct.sz[x];
lct.cut(x,f[x]);lct.link(y,f[x]);
lct.link(x,y);lct.link(v,y);
l[y]=l[u]+1;f[y]=f[x];f[x]=f[v]=y;
memcpy(ch[y],ch[x],sizeof(ch[x]));
while(u&&ch[u][c]==x) {ch[u][c]=y;u=f[u];}
}
void add(int from,int to)
{
g[++num].next=last[from];
g[num].to=to;
last[from]=num;
}
void dfs(int x)
{
for(int i=last[x];i;i=g[i].next)
{
int v=g[i].to;
dfs(v);
sz[x]+=sz[v];
}
}
}sam;
signed main()
{
int q=read();
scanf("%s",a+1);
int mask=0,ans,len=strlen(a+1);
for(int i=1;i<=len;i++){
sam.insert(a[i]-'A',i);
lct.split(sam.lt,1);
lct.tag_add[1]+=1;
lct.val[1]+=1;
lct.sum[1]+=lct.sz[1];
}
while(q--)
{
string op;cin>>op;
if(op=="ADD"){
cin>>a;
Mask(a,mask);int l=strlen(a);
for(int i=0;i<l;i++){
sam.insert(b[i]-'A',i);
lct.split(sam.lt,1);
lct.tag_add[1]+=1;
lct.val[1]+=1;
lct.sum[1]+=lct.sz[1];
}
}
if(op=="QUERY"){
cin>>a;
Mask(a,mask); int u=1,l=strlen(a);
for(int i=0;i<l;i++){
int c=b[i]-'A';
u=sam.ch[u][c];
}
if(!u) ans=0;else{lct.split(u,u);ans=lct.sum[u];}
cout<<ans<<endl;
mask=mask^ans;
}
}
return 0;
}