• BZOJ.2555.SubString(后缀自动机 LCT)


    题目链接

    (Description)

    给你一个字符串init,要求支持两个操作:
    (1)在当前字符串的后面插入一个字符串s
    (2)询问字符串s在当前字符串中出现了几次(作为连续子串)
    强制在线。

    (Solution)

    对于每次询问,串(s)出现的次数就是(s)在SAM上走完后到达节点的(|right|)(SAM上是模板串的所有子串的状态啊 没有转移那就是匹配不上了)。
    于是就成了动态维护(right)集合的大小。
    暴力的话,每次加入一个字符 只需要沿着(fa)向上++(|right|)就可以了。要注意复制节点(nq)的时候(|right|)也要复制。
    现在需要用LCT来动态维护parent树,并支持更新点到根这条链的值,连边删边。询问的时候直接输出到达节点的size即可。

    这棵树是有根树,所以不需要Make_root()
    别忘了(sz)维护的是到叶节点的size(注意(sz)的更新);改变SAM中的(fa)时都要修改parent树。
    对于(sz)初值:(sz[np]=1)(sz[nq]=0)。想想以前不就是这样吗,只对每个(np)走到的初始化为(1).

    //163128kb	16108ms
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    const int N=(6e5+5)*2;
    
    namespace LCT
    {
    	#define lson son[x][0]
    	#define rson son[x][1]
    	int fa[N],son[N][2],sz[N],tag[N],sk[N];
    
    	inline void Update(int x,int v){
    		sz[x]+=v, tag[x]+=v;
    	}
    	inline void PushDown(int x){
    		if(tag[x]) Update(lson,tag[x]), Update(rson,tag[x]), tag[x]=0;
    	}
    	inline bool n_root(int x){
    		return son[fa[x]][0]==x||son[fa[x]][1]==x;
    	}
    	void Rotate(int x)
    	{
    		int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
    		if(n_root(a)) son[b][son[b][1]==a]=x;
    		if(son[x][r]) fa[son[x][r]]=a;
    		fa[x]=b, fa[a]=x, son[a][l]=son[x][r], son[x][r]=a;
    	}
    	void Splay(int x)
    	{
    		int a=x,t=1; sk[1]=x;
    		while(n_root(a)) sk[++t]=a=fa[a];
    		while(t) PushDown(sk[t--]);
    		while(n_root(x))
    		{
    			if(n_root(a=fa[x])) Rotate(son[a][1]==x^son[fa[a]][1]==a?x:a);
    			Rotate(x);
    		}
    	}
    	void Access(int x){
    		for(int pre=0; x; x=fa[pre=x])
    			Splay(x), rson=pre;//, Update(x);
    	}
    	void Link(int x,int y){//Link x->y
    		Access(y), Splay(y), fa[x]=y, Update(y,sz[x]);
    	}
    	void Cut(int x){//Cut x->fa[x]
    		Access(x), Splay(x), Update(lson,-sz[x]), lson=fa[lson]=0;//Update()!
    	}
    }
    struct Suffix_Automaton
    {
    	int l,tot,las,Mask,fa[N],son[N][26],len[N];
    	char s[3000005];
    	void Insert(int c)
    	{
    		int p=las,np=++tot;
    		len[las=np]=len[p]+1, LCT::sz[np]=1;
    		for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;
    		if(!p) fa[np]=1, LCT::Link(np,1);
    		else
    		{
    			int q=son[p][c];
    			if(len[q]==len[p]+1) fa[np]=q, LCT::Link(np,q);
    			else
    			{
    				int nq=++tot; len[nq]=len[p]+1;
    				memcpy(son[nq],son[q],sizeof son[q]);
    				fa[nq]=fa[q], LCT::Link(nq,fa[q]);
    				fa[np]=fa[q]=nq, LCT::Cut(q), LCT::Link(np,nq), LCT::Link(q,nq);
    				for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;
    			}
    		}
    	}
    	inline void Decode_With_Mask(int mask)//mask是传参 
    	{
    		for(int i=0; i<l; ++i)
    			mask=(mask*131+i)%l, std::swap(s[i+1],s[mask+1]);
    	}
    	void Init()
    	{
    		tot=las=1, Mask=0;
    		scanf("%s",s+1), l=strlen(s+1);
    		for(int i=1; i<=l; ++i) Insert(s[i]-'A');
    	}
    	void Build()
    	{
    		scanf("%s",s+1), l=strlen(s+1);
    		Decode_With_Mask(Mask);
    		for(int i=1; i<=l; ++i) Insert(s[i]-'A');
    	}
    	int Walk()
    	{
    		int p=1;
    		for(int i=1; i<=l; ++i)
    			if(!(p=son[p][s[i]-'A'])) return 0;
    		LCT::Splay(p);
    		return Mask^=LCT::sz[p], LCT::sz[p];
    	}
    	void Query()
    	{
    		scanf("%s",s+1), l=strlen(s+1);
    		Decode_With_Mask(Mask);
    		printf("%d
    ",Walk());
    	}
    }sam;
    
    int main()
    {
    	int Q; char opt[8];
    	scanf("%d",&Q), sam.Init();
    	while(Q--){
    		if(scanf("%s",opt),opt[0]=='A') sam.Build();
    		else sam.Query();
    	}
    	return 0;
    }
    
  • 相关阅读:
    ubuntu--基础环境瞎搞集合
    java _tomcat_mysql 部署
    简单Dp----最长公共子序列,DAG最长路,简单区间DP等
    大素数判断和素因子分解(miller-rabin,Pollard_rho算法)
    ssh 命令
    linux服务器上设置多主机头,设置多web站点
    getline()函数
    SGU[118] Digital Root
    SGU[117] Counting
    SGU[104] Little shop of flowers
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9239427.html
Copyright © 2020-2023  润新知