bzoj2555 Substring

传送门

询问某一个点的出现次数即为求它在 S A M SAM SAM上对应节点的 e n d p o s endpos endpos集合大小。于是只需要维护 p a r e n t parent parent树的 e n d p o s endpos endpos
当我们加入一个字符时,可能会涉及到断父亲,连父亲的操作。并且由于新加的这个点有一个新的 e n d p o s endpos endpos的贡献,需要给它到根节点的路径加 1 1 1

这恰恰是 L C T LCT LCT可以干的事情:断边,连边,链加。这里的链加恰好是当前点到根节点全部加 1 1 1,于是 a c c e s s access access然后 s p l a y splay splay再打个标记即可。

代码其实就是把 S A M SAM SAM b u i l d build build的操作在 L C T LCT LCT上模拟一下。写的时候建议先把 S A M SAM SAM写完,然后照着 b u i l d build build的流程写出在 L C T LCT LCT上的操作,记得 b u i l d build build之后要打标记。

甚至没有 p u s h u p pushup pushup

#include<bits/stdc++.h>
using namespace std;
const int maxn=6e6+10,ch_set=26;
int Q,mask=0,len,ans;string chars;char s[maxn];
inline void decode(int mask){
	scanf("%s",s),chars=s;
	for(int j=0;j<chars.length();++j){
		mask=(mask*131+j)%chars.length();
		swap(chars[j],chars[mask]);
	}
}
namespace LCT{
	int fa[maxn],son[maxn][2],add[maxn],val[maxn],st[maxn],top;
	inline int get(int x){return x==son[fa[x]][1];}
	inline void pushdown(int x){
		if(add[x]){
			if(son[x][0]) add[son[x][0]]+=add[x],val[son[x][0]]+=add[x];
			if(son[x][1]) add[son[x][1]]+=add[x],val[son[x][1]]+=add[x];
			add[x]=0;
		}
	}
	inline int isroot(int x){return (!fa[x])||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x);}
	inline void rotate(int x){
		int y=fa[x],z=fa[y],k=get(x),l=son[x][k^1];
		if(!isroot(y)) son[z][get(y)]=x;
		fa[x]=z,fa[y]=x,son[x][k^1]=y;
		fa[l]=y,son[y][k]=l;
	}
	inline void splay(int x){
		st[top=1]=x;
		for(int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
		while(top) pushdown(st[top--]);
		for(int y=fa[x];!isroot(x);rotate(x),y=fa[x])
			if(!isroot(y)) rotate(get(x)==get(y)?y:x);
	}
	inline void access(int x){
		for(int y=0;x;y=x,x=fa[x])
			splay(x),son[x][1]=y;
	}
	//将y置做x的父亲。 
	inline void link(int x,int y){fa[x]=y;}
	//江x与x的父亲断开。 
	inline void cut(int x){
		access(x),splay(x);
		son[x][0]=fa[son[x][0]]=0;
	}
	inline void pushnow(int x){
		access(x),splay(x);
		++val[x],++add[x];
	}
	inline int query(int x){return access(x),splay(x),val[x];}
}
namespace SAM{
	int last,sz;const int root=1;
	struct node{int link,nxt[ch_set],len;}st[maxn];
	inline void init(){
		last=root,sz=root;
		st[root].link=0,st[root].len=0;
		memset(st[root].nxt,0,sizeof(st[root].nxt));
	}
	inline void build(int c){
		int cur=++sz,p=last;st[cur].len=st[last].len+1;
		for(;p&&!st[p].nxt[c];p=st[p].link) st[p].nxt[c]=cur;
		if(!p) st[cur].link=root,LCT::link(cur,root);
		else{
			int q=st[p].nxt[c];
			if(st[p].len+1==st[q].len) st[cur].link=q,LCT::link(cur,q);
			else{
				int clone=++sz;st[clone]=st[q],st[clone].len=st[p].len+1;
				LCT::cut(q),LCT::val[clone]=LCT::val[q],LCT::link(clone,st[q].link);
				for(;p&&st[p].nxt[c]==q;p=st[p].link)
					st[p].nxt[c]=clone;
				st[q].link=st[cur].link=clone;
				LCT::link(q,clone),LCT::link(cur,clone);
			}
		}last=cur;
	}
	inline int query(string t){
		int now=root;
		for(int i=0;i<t.length();++i){
			if(!st[now].nxt[t[i]-'A']) return 0;
			now=st[now].nxt[t[i]-'A'];
		}return LCT::query(now);
	}
}
int main(){
	//freopen("bzoj2555.in","r",stdin);
	scanf("%d%s",&Q,s),len=strlen(s),SAM::init();
	for(int i=0;i<len;++i) SAM::build(s[i]-'A'),LCT::pushnow(SAM::last);
	while(Q--){
		scanf("%s",s);
		if(s[0]=='A'){
			decode(mask);
			for(int i=0;i<chars.length();++i)
				SAM::build(chars[i]-'A'),LCT::pushnow(SAM::last);
		}
		if(s[0]=='Q'){
			decode(mask),ans=SAM::query(chars);
			printf("%d\n",ans),mask^=ans;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值