2555: SubString
Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 1374 Solved: 410
[Submit][Status][Discuss]
Description
懒得写背景了,给你一个字符串init,要求你支持两个操作
(1):在当前字符串的后面插入一个字符串
(2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
你必须在线支持这些操作。
Input
第一行一个数Q表示操作个数
第二行一个字符串表示初始字符串init
接下来Q行,每行2个字符串Type,Str
Type是ADD的话表示在后面插入字符串。
Type是QUERY的话表示询问某字符串在当前字符串中出现了几次。
为了体现在线操作,你需要维护一个变量mask,初始值为0
读入串Str之后,使用这个过程将之解码成真正询问的串TrueStr。
询问的时候,对TrueStr询问后输出一行答案Result
然后mask = mask xor Result
插入的时候,将TrueStr插到当前字符串后面即可。
HINT:ADD和QUERY操作的字符串都需要解压
Output
Sample Input
2
A
QUERY B
ADD BBABBBBAAB
Sample Output
0
HINT
40 % 的数据字符串最终长度 <= 20000,询问次数<= 1000,询问总长度<= 10000
100 % 的数据字符串最终长度 <= 600000,询问次数<= 10000,询问总长度<= 3000000
网上的题解都写的是
后缀平衡树就是一个可以维护后缀字典序大小关系的平衡树。也就是后缀数组中的
对于这道题来说,因为每次需要在最后加一个字符串,所以可以将字符串倒过来,这样就相当于每次加入一些后缀。每次把这个后缀插入到平衡树中去。每走到一个节点,需要比较字典序的大小,这里就可以用另一种求
查询的时候可以在在查询的字符串后加一个很小的字符,查一次排名,删掉小的再加一个大的查一次,两次的排名相减就是答案。为了方便写平衡树里的比较函数,我每次查询的时候都先把这个串接到原串的后面,查完之后再删掉。
时间复杂度:
但是这个题刚写完一直
①:平衡树一定要开内存池,每次
②:这道题用
③:在每次二分的时候,可以加一点优化:可以先看一下前
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
#define LL unsigned int
#define p 233LL
#define mid (l+r)/2
const int N=4000010;
char s[N],ss[N];
int T,n,ans,flag,mask,cnt,tp;
LL hash[N],pow[N];
struct Node{
LL v;
Node *ch[2];
int r,num,sum,len,No;
inline int cmp(LL x,int now,int Len){
int l=1,r=min(len,Len),maxn=0;
if(!flag) r=min(r,Len-n);
if(r>25){
LL o0=v-((No-25)<0?0:hash[No-25]*pow[25]);
LL o1=x-((now-25)<0?0:hash[now-25]*pow[25]);
if(o0!=o1) r=25;
else l=maxn=25;
}
while(l<=r){
LL o0=v-((No-mid)<0?0:hash[No-mid]*pow[mid]);
LL o1=x-((now-mid)<0?0:hash[now-mid]*pow[mid]);
if(o0==o1) maxn=max(maxn,mid),l=mid+1;
else r=mid-1;
}
if(!flag){
if(maxn==Len-n-1){
if(s[Len-maxn-1]==(char)1) return 0;
else return 1;
}
}
if(maxn==Len) return Len==len?-1:0;
if(maxn==len) return Len==len?-1:1;
return s[No-maxn]<s[now-maxn]?1:0;
}
inline void Maintain(){
sum=num;
if(ch[0]!=NULL) sum+=ch[0]->sum;
if(ch[1]!=NULL) sum+=ch[1]->sum;
}
}*root,pool[N];
inline void rotate(Node* &o,int d){
Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;
o->Maintain();k->Maintain();o=k;
}
inline void insert(Node* &o,LL x,int now,int kind,int Len){
if(o==NULL){
o=&pool[++tp];
o->ch[0]=o->ch[1]=NULL;
o->r=rand();
o->num=1;
o->v=x;
o->len=Len;
o->No=now;
}
else{
int d=o->cmp(x,now,Len);
if(d==-1) o->num+=1;
else{
insert(o->ch[d],x,now,kind,Len);
if(o->r < o->ch[d]->r) rotate(o,d^1);
}
}
o->Maintain();
}
inline int rank(Node* &o,LL x,int now,int Len){
if(o==NULL) return 0;
int d=o->cmp(x,now,Len);
if(d==-1) return (o->ch[0]==NULL?1:o->ch[0]->sum+1);
else{
if(d==0) return rank(o->ch[0],x,now,Len);
else return (rank(o->ch[1],x,now,Len)+(o->ch[0]==NULL?o->num:o->ch[0]->sum+o->num));
}
}
inline void in(int kind){
flag=kind;++cnt;
int now=0,i,pre=mask;
char ch=getchar();
while(ch<'A'||ch>'Z') ch=getchar();
while(ch>='A'&&ch<='Z'){
//s[n]=ch;
ss[now]=ch;
//pow[n]=n?(pow[n-1]*p):1LL;
//hash[n]=(n?(hash[n-1]*p):0LL)+(LL)s[n];
//if(kind) insert(root,hash[n],n,kind,n+1);
++now,ch=getchar();
}
for(i=0;i<now;++i){
if(cnt==1) continue;
mask=(mask*131+i)%now;
char t=ss[i];
ss[i]=ss[mask];
ss[mask]=t;
}
for(i=0;i<now;++i){
s[n]=ss[i];
pow[n]=n?(pow[n-1]*p):1LL;
hash[n]=(n?(hash[n-1]*p):0LL)+(LL)s[n];
if(kind) insert(root,hash[n],n,kind,n+1);
++n;
}
mask=pre;
if(!kind){
n-=now;
pow[n]=pow[n-1]*p;
hash[n]=hash[n-1]*p+1LL;
for(i=now-1;~i;--i) s[i+n+1]=s[i+n];
for(s[n]=(char)1,i=n+1;i<=n+now;++i)
pow[i]=pow[i-1]*p,hash[i]=hash[i-1]*p+(LL)s[i];
ans=-rank(root,hash[n+now],now+n,now+n+1);
hash[n]=hash[n-1]*p+126LL;
for(s[n]=(char)126,i=n+1;i<=n+now;++i)
pow[i]=pow[i-1]*p,hash[i]=hash[i-1]*p+(LL)s[i];
ans+=rank(root,hash[n+now],now+n,now+n+1);
for(i=0;i<now;++i) s[n+i]=s[n+i+1];
mask^=ans;
printf("%d\n",ans);
}
}
int main(){
int i,j;
scanf("%d",&T);
s[0]=(char)1;
in(1);
while(T--){
char o[10],ch=getchar(),len=0;
while(ch<'A'||ch>'Z') ch=getchar();
while(ch>='A'&&ch<='Z') o[len++]=ch,ch=getchar();
if(o[0]=='Q') in(0);
else in(1);
}
}