2555: SubString
Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 2857 Solved: 856
[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
新加数据一组–2015.05.20
Source
Ctsc模拟赛By 洁妹
ps:这篇题解是纪念我学的后缀平衡树,主要提了提后缀平衡树的用法。
sol:
题目的强制在线有坑点,mask只有在mask = mask xor Result时改变结果,其他时候都是局部变量。
这道题我当做后缀平衡树的裸题来做了。
下面阐述一下后缀平衡树
节点编号x存的是字符在字符串中的位置,这个x在树中的位置表示其后缀的大小,后缀平衡树只支持在前面插字符,这相当于是插入了一个后缀。如果题目要求在后面插,那么我们可以视作这个字符串的后缀为其前缀,做起来没差别。
我的后缀平衡树使用treap来达到重量平衡。使得保证树的形态的同时,使其为key(rand)值的小根堆。
后缀平衡树支持几个经典操作
1:插入字符(即某个后缀)
2:查询某个后缀(可以不包含在本串中)在树中的排名
3:查询排名为x的某个后缀
后缀平衡树的实质为动态的sa,因此sa支持的操作后缀平衡树都支持。
1:
朴素算法:插入字符时,要先和当前节点的后缀比较大小,一位一位的比较,直到某位不同时比出大小。这个显然太慢
优化算法:我们发现只需要比较第一位,之后的后缀在之前处理过。因此我们可以通过询问2个后缀的排名来比较,实现log^2的插入。
优越算法:我们对后缀平衡树中的每个节点进行规定,设其区间为(l,r),则这个节点的值为mid,左儿子的区间为(l,mid),右儿子为(mid+1,r),值直接代表该后缀在后缀平衡树中的相对大小。则我们在插入字符时可以O(1)的比较后缀。
我们应该怎么实现这个标号法呢?由于重量平衡的性质,每个点的子树大小期望为O(1),因此我们在每次旋转操作的时候,可以直接暴力修改整个子树的标号值。因为重量平衡的性质,所以树高为log级别,标号每次都/2,所以我们开long long就能够把区间存下来了。
2:
查询某个后缀的排名,就是带着一个串在后缀平衡树上走。
朴素算法:和当前节点一位一位比较
优化算法(假的):二分,用字符串哈希求LCP比较。
需要注意的是,朴素算法一般比优化算法快。
3:
从这个节点往上走记录size即可。
正文:
本题要求在字符串后面插入字符,那就把前缀看成后缀即可。
求字符串x在文本串中出现了几次,实际上就是求
小于x#的后缀的数目 - 小于x的后缀的数目(#为最大字符)
本题前缀是后缀,就是把#放前面
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;
const int N=600005;
int n,m,rt,tot;
ll rank[N];
struct tree{int son[2],key;}a[N];
int key[N];
char sr[N];
inline int read()
{
char c;
bool flag=false;
while((c=getchar())>'9'||c<'0')
if(c=='-')flag=true;
int res=c-'0';
while((c=getchar())>='0'&&c<='9')
res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
int size[N];
inline void updata(int x,ll l,ll r)
{
if(!x) return;
size[x]=1;
rank[x]=l+r>>1;
updata(a[x].son[0],l,rank[x]);
updata(a[x].son[1],rank[x]+1,r);
size[x]=size[a[x].son[0]]+size[a[x].son[1]]+1;
}
inline bool cmp(int x,int y)
{
return sr[x]<sr[y]||sr[x]==sr[y]&&rank[x-1]<rank[y-1];
}
inline void rotate(int x,int y,ll l,ll r)
{
int Side=a[y].son[1]==x;
a[y].son[Side]=a[x].son[Side^1];
a[x].son[Side^1]=y;
updata(x,l,r);
}
inline void insert(int &x,ll l,ll r)
{
ll mid=l+r>>1;
if(!x)
{
x=++tot;
size[x]=1;
a[x].key=rand();
rank[x]=mid;
return;
}
size[x]++;
if(cmp(tot+1,x)) insert(a[x].son[0],l,mid);
else insert(a[x].son[1],mid+1,r);
if(a[tot].key<a[x].key)
{
rotate(tot,x,l,r);
x=tot;
}
}
int l,len,mask,lastAns=0;
char s[N],Sr[N];
inline void get_ready(int mask)
{
scanf("%s",s);
l=strlen(s);
for(int i=0;i<l;++i)
{
mask=(mask*131+i)%l;
swap(s[i],s[mask]);
}
}
int P=2333;
inline void lcp(int x,int &LCP)
{
int e=min(l+1,x);
for(int i=1;i<=e;++i)
if(sr[x-i+1]==s[l-i+1]) LCP++;
else break;
}
inline int cmp_(int x)//1表示x比较小 0表示大 2=
{
if(sr[x]!=s[l]) return sr[x]<s[l];
int LCP=0;
lcp(x,LCP);
if(LCP==l+1&&LCP==x) return 2;
if(LCP==l+1) return 0;
if(LCP==x) return 1;
return sr[x-LCP]<s[l-LCP];
}
inline int get_rank(int x)
{
if(!x) return 0;
int flag=cmp_(x);
if(flag==1) return size[a[x].son[0]]+1+get_rank(a[x].son[1]);
else if(flag==0) return get_rank(a[x].son[0]);
else if(flag==2) return size[a[x].son[0]];
}
int main()
{
// freopen("SubString.in","r",stdin);
// freopen("SubString.out","w",stdout);
srand(19260817);
m=read();
scanf("%s",sr+1);
len=strlen(sr+1);
for(int i=1;i<=len;++i) insert(rt,1,(ll)1<<62);
while(m--)
{
scanf("%s",Sr+1);
get_ready(mask);
if(Sr[1]=='A')
for(int j=0;j<l;++j)
{
sr[++len]=s[j];
insert(rt,1,(ll)1<<62);
}
if(Sr[1]=='Q')
{
if(l>len)
{
lastAns=0;
printf("%d\n",lastAns);
mask^=lastAns;
continue;
}
--l;
lastAns=-get_rank(rt);
for(int j=l;j>=0;--j)
s[j+1]=s[j];
s[0]=(char)127;
++l;
lastAns+=get_rank(rt);
printf("%d\n",lastAns);
mask^=lastAns;
}
}
}
后缀自动机
有生之年还是学了后缀自动机
关于后缀自动机的部分可以看一下我的小结。
考虑这题怎么做,因为要维护right集合大小,所以在np代替p的时候,要更新一下np一直到root的parent,这个东西用lct维护一下即可。
我不是很清楚我一开始维护的是子树信息,但是那种做法错了,后来改成了链的做法就过了。
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
const int N=1210000;
int last=1,root=1,tot=1;
void ins(int x,int y,int v);
inline int read()
{
char c;
bool pd=0;
while((c=getchar())>'9'||c<'0')
if(c=='-') pd=1;
int res=c-'0';
while((c=getchar())>='0'&&c<='9')
res=(res<<3)+(res<<1)+c-'0';
return pd?-res:res;
}
int lc[N],rc[N],sum1[N],sum2[N],fa[N],real[N],add[N];
bool rev[N];
inline bool is_root(int x)
{
return lc[fa[x]]!=x&&rc[fa[x]]!=x;
}
inline void tag_rev(int x)
{
rev[x]=!rev[x];
swap(lc[x],rc[x]);
}
inline void tag_down(int x)
{
if(rev[x])
{
tag_rev(lc[x]);
tag_rev(rc[x]);
rev[x]=0;
}
if(add[x])
{
add[lc[x]]+=add[x];
add[rc[x]]+=add[x];
sum1[lc[x]]+=add[x];
sum1[rc[x]]+=add[x];
add[x]=0;
}
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int b=lc[y]==x?rc[x]:lc[x];
if(b) fa[b]=y;
fa[x]=z;fa[y]=x;
if(z)
{
if(lc[z]==y) lc[z]=x;
if(rc[z]==y) rc[z]=x;
}
if(lc[y]==x) lc[y]=b,rc[x]=y;
else rc[y]=b,lc[x]=y;
}
int sta[N];
inline void splay(int x)
{
sta[sta[0]=1]=x;
for(int y=x;!is_root(y);y=fa[y]) sta[++sta[0]]=fa[y];
while(sta[0]) tag_down(sta[sta[0]--]);
while(!is_root(x))
{
if(!is_root(fa[x]))
{
if((lc[fa[x]]==x)==(lc[fa[fa[x]]]==fa[x])) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}
inline void access(int q)
{
for(int p=0;q;p=q,q=fa[q])
{
splay(q);
sum2[q]+=sum1[rc[q]];
rc[q]=p;
sum2[q]-=sum1[rc[q]];
}
}
inline void make_root(int x)
{
access(x);
splay(x);
tag_rev(x);
}
inline void link(int x,int y)
{
make_root(x);
fa[x]=y;
}
inline void cut(int x,int y)
{
make_root(x);
access(y);
splay(x);
rc[x]=fa[y]=0;
}
inline void ins(int x,int y,int v)
{
make_root(x);
access(y);
splay(x);
add[x]+=v;
sum1[x]+=v;
}
struct cc
{
int val,go[26],par;
}state[N];
inline void extend(int c)
{
++m;
int p=last;
int np=++tot;
last=np;
state[np].val=m;
real[np]=1;
sum1[np]=1;
while(p&&!state[p].go[c]) {state[p].go[c]=np;p=state[p].par;}
if(!p) {state[np].par=root;link(np,root);ins(root,root,1);return;}
int q=state[p].go[c];
if(state[q].val==state[p].val+1) {state[np].par=q;link(np,q);ins(q,root,1);return;}
int nq=++tot;
splay(q);
sum1[nq]=sum1[q];
state[nq].val=state[p].val+1;
for(int i=0;i<=25;++i) state[nq].go[i]=state[q].go[i];
state[nq].par=state[q].par;
link(nq,state[q].par);
cut(q,state[q].par);
link(q,nq);
state[q].par=nq;
link(np,nq);
state[np].par=nq;
ins(nq,root,1);
while(state[p].go[c]==q) state[p].go[c]=nq,p=state[p].par;
}
int mask;
char sr[N];
inline void get(int mask)
{
scanf("%s",sr);
n=strlen(sr);
for(int j=0;j<n;++j)
{
mask=(mask*131+j)%n;
swap(sr[j],sr[mask]);
}
}
int main()
{
// freopen("2555.in","r",stdin);
int q=read();
scanf("%s",sr+1);
n=strlen(sr+1);
for(int i=1;i<=n;++i) extend(sr[i]-'A');
for(int i=1;i<=q;++i)
{
scanf("%s",sr+1);
if(sr[1]=='A')
{
get(mask);
for(int j=0;j<n;++j)
extend(sr[j]-'A');
}
else
{
get(mask);
int now=root,ans=0;
for(int j=0;j<n;++j)
{
if(!state[now].go[sr[j]-'A'])
{
ans=-1;
break;
}
now=state[now].go[sr[j]-'A'];
}
if(ans==-1) {printf("0\n");continue;}
// make_root(root);
// access(now);
// splay(root);
splay(now);
printf("%d\n",sum1[now]);
mask^=sum1[now];
}
}
}