zoukankan      html  css  js  c++  java
  • SA的一个辣鸡trick

    基础板子

    namespace SA{
        int x[400010],y[400010],SA[400010],rk[400010],ht[400010],t[400010];
        int st[19][400010],lg[400010];
        il int getLCP(int x,int y){
            if(x==y)return 1e9;
            int l=lg[y-x];
            return std::min(st[l][x],st[l][y-(1<<l)]);
        }
        il vd getSA(){
            int set=400000;
            for(int i=1;i<=N;++i)++t[x[i]=S[i]];
            for(int i=1;i<=set;++i)t[i]+=t[i-1];
            for(int i=N;i;--i)SA[t[x[i]]--]=i;
            for(int k=1;k<=N;k<<=1){
                int p=0;
                for(int i=N-k+1;i<=N;++i)y[++p]=i;
                for(int i=1;i<=N;++i)if(SA[i]>k)y[++p]=SA[i]-k;
                for(int i=1;i<=set;++i)t[i]=0;
                for(int i=1;i<=N;++i)++t[x[y[i]]];
                for(int i=1;i<=set;++i)t[i]+=t[i-1];
                for(int i=N;i;--i)SA[t[x[y[i]]]--]=y[i];
                std::swap(x,y);x[SA[1]]=p=1;
                for(int i=2;i<=N;++i){
                    if(y[SA[i]]!=y[SA[i-1]]||y[SA[i]+k]!=y[SA[i-1]+k])++p;
                    x[SA[i]]=p;
                }
                if(p>=N)break;set=p;
            }
            for(int i=1;i<=N;++i)rk[SA[i]]=i;
            for(int i=1,j,k;i<=N;++i){
                if(rk[i]==N)continue;
                if(k)--k;
                j=SA[rk[i]+1];
                while(S[i+k]==S[j+k])++k;
                ht[rk[i]]=k;
            }
            for(int i=2;i<=N;++i)lg[i]=lg[i>>1]+1;
            for(int i=1;i<=N;++i)st[0][i]=ht[i];
            for(int i=1;i<=lg[N];++i)
                for(int j=1;j+(1<<i)-1<=N;++j)
                    st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<i-1)]);
        }
    }
    

    用SA给后缀排序以后,就可以处理“和一个后缀的LCP>=k的所有后缀的信息和”这样的问题了,因为和一个后缀的LCP>=k的所有后缀在SA数组上是连续的一段。

    但是有一些题目并不能做,只能用sam建树,加上线段树合并做。

    因为sam维护的是子树查询,而SA这个做法是区间询问,子树查询的性质当然比区间询问更好然而我不会sam

    然后我发现我sb了,其实SA这个“和一个后缀的LCP>=k的所有后缀”的区间肯定只有相互包含和不交这两种关系,所以这也是一棵树= =如果建出来这个树就可以和SAM一样做了。

    建树也很简单= =NOIP2018铺设道路

    然后就也能够处理这样的东西了,和后缀x的LCP>=k的所有后缀直接倍增一下查询那个点,和sam差不多

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    #define ll long long
    il int gi(){
    	int x=0,f=0;char ch=getchar();
    	while(!isdigit(ch))f^=ch=='-',ch=getchar();
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return f?-x:x;
    }
    char s[100010];
    int n,Q;
    namespace SA{
    	int SA[100010],rk[100010],ht[100010],Log[100010],st[17][100010];
    	il int LCP(int x,int y){
    		if(x==y)return 1e9;int l=Log[y-x];
    		return std::min(st[l][x],st[l][y-(1<<l)]);
    	}
    	il vd getSA(){
    		static int x[100010],y[100010],t[100010];
    		struct gety{il int operator()(int x){return x<=n?y[x]:-1;}}gety;
    		int set=128;
    		for(int i=1;i<=n;++i)++t[x[i]=s[i]];
    		for(int i=1;i<=set;++i)t[i]+=t[i-1];
    		for(int i=n;i;--i)SA[t[x[i]]--]=i;
    		for(int k=1;k<=n;k<<=1){
    			int p=0;
    			for(int i=n-k+1;i<=n;++i)y[++p]=i;
    			for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;
    			for(int i=1;i<=set;++i)t[i]=0;
    			for(int i=1;i<=n;++i)++t[x[y[i]]];
    			for(int i=1;i<=set;++i)t[i]+=t[i-1];
    			for(int i=n;i;--i)SA[t[x[y[i]]]--]=y[i];
    			std::swap(x,y);x[SA[1]]=p=1;
    			for(int i=2;i<=n;++i){
    				if(gety(SA[i])!=gety(SA[i-1])||gety(SA[i]+k)!=gety(SA[i-1]+k))++p;
    				x[SA[i]]=p;
    			}
    			if(p==n)break;set=p;
    		}
    		for(int i=2;i<=n;++i)Log[i]=Log[i>>1]+1;
    		for(int i=1;i<=n;++i)rk[SA[i]]=i;
    		for(int i=1,j,k=0;i<=n;++i){
    			if(rk[i]==n)continue;
    			if(k)--k;j=SA[rk[i]+1];
    			while(s[i+k]==s[j+k])++k;
    			ht[rk[i]]=k;
    		}
    		for(int i=1;i<=n;++i)st[0][i]=ht[i];
    		for(int i=1;i<=Log[n];++i)
    			for(int j=1;j+(1<<i)-1<=n;++j)
    				st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<i-1)]);
    	}
    }
    int cnt;
    int fir[200010],dis[200010],nxt[200010],id;
    il vd link(int a,int b){nxt[++id]=fir[a],fir[a]=id,dis[id]=b;}
    int depest[200010],sL[200010],sR[200010],sP[200010];
    int st[18][200010],siz[200010],son[200010];
    il int buildTree(int L,int R){
    	int x=++cnt;sL[x]=L,sR[x]=R;siz[x]=1;
    	if(L==R){sP[x]=1e9;return depest[L]=x;}
    	int p=SA::LCP(L,R),l,r,mid;sP[x]=p;
    	for(int i=L;i<=R;++i){
    		l=i,r=R;
    		while(l<r){
    			mid=((l+r)>>1)+1;
    			if(SA::LCP(i,mid)>p)l=mid;
    			else r=mid-1;
    		}
    		int t=buildTree(i,l);st[0][t]=x;siz[x]+=siz[t];
    		if(siz[son[x]]<siz[t])son[x]=t;
    		link(x,t);
    		i=l;
    	}
    	return x;
    }
    
  • 相关阅读:
    Linux 常用工具openssh之ssh-add
    Linux 常用工具openssh之scp
    Linux 常用工具openssh之ssh
    Linux 常用工具sysstat之sar
    Shell常用命令之sort
    Docker存储驱动之Device Mapper简介
    ceph-deploy install时,远端节点在执行apt-get update命令时失败
    Ceph osd启动报错osd init failed (36) File name too long
    rdb map出错rbd sysfs write failed
    Docker存储驱动之OverlayFS简介
  • 原文地址:https://www.cnblogs.com/xzz_233/p/10647278.html
Copyright © 2011-2022 走看看