zoukankan      html  css  js  c++  java
  • [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)

    后缀数组解法:

    先二分最长前缀长度 (len),然后从 (rnk[c]) 向左右二分 (l)(r) 使 ([l,r])(heightgeq len),然后在主席树上查 (sa[l..r]) 是否有 (a..b) 中的任意一个数。时间复杂度 (O(nlog^2 n))

    (Code Below:)

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=100000+10;
    int n,m,sa[maxn],tax[maxn],rnk[maxn],tp[maxn],h[maxn],f[maxn][18],g[maxn][18];
    int T[maxn],L[maxn*20],R[maxn*20],sum[maxn*20],cnt;char s[maxn];
    
    inline int read(){
    	register int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return (f==1)?x:-x;
    }
    
    void SA(int m){
    	int i,j,k,p,d=0;
    	for(i=1;i<=n;i++) rnk[i]=s[i];
    	for(i=1;i<=n;i++) tax[rnk[i]]++;
    	for(i=1;i<=m;i++) tax[i]+=tax[i-1];
    	for(i=n;i>=1;i--) sa[tax[rnk[i]]--]=i;
    	for(k=1,p=0;p<n;m=p,k<<=1){
    		p=0;
    		for(i=n-k+1;i<=n;i++) tp[++p]=i;
    		for(i=1;i<=n;i++) if(sa[i]>k) tp[++p]=sa[i]-k;
    		for(i=1;i<=m;i++) tax[i]=0;
    		for(i=1;i<=n;i++) tax[rnk[i]]++;
    		for(i=1;i<=m;i++) tax[i]+=tax[i-1];
    		for(i=n;i>=1;i--) sa[tax[rnk[tp[i]]]--]=tp[i];
    		swap(rnk,tp);rnk[sa[1]]=p=1;
    		for(i=2;i<=n;i++) rnk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+k]==tp[sa[i]+k])?p:++p;
    	}
    	for(i=1;i<=n;i++) rnk[sa[i]]=i;
    	for(i=1;i<=n;i++){
    		p=sa[rnk[i]-1];if(d) d--;
    		while(s[i+d]==s[p+d]) d++;
    		h[rnk[i]]=d;
    	} 
    	for(i=1;i<=n;i++) f[i][0]=h[i+1],g[i][0]=h[i];
    	for(j=1;j<=17;j++){
    		for(i=1;i+(1<<j)-1<=n;i++) f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    		for(i=(1<<j);i<=n;i++) g[i][j]=min(g[i][j-1],g[i-(1<<(j-1))][j-1]);
    	}
    }
    
    void update(int &now,int pre,int l,int r,int x){
    	now=++cnt;L[now]=L[pre];R[now]=R[pre];sum[now]=sum[pre]+1;
    	if(l == r) return ;
    	int mid=(l+r)>>1;
    	if(x <= mid) update(L[now],L[pre],l,mid,x);
    	else update(R[now],R[pre],mid+1,r,x);
    }
    
    int query(int u,int v,int Le,int Ri,int l,int r){
    	if(Le <= l && r <= Ri) return sum[v]-sum[u];
    	int mid=(l+r)>>1,ans=0;
    	if(Le <= mid) ans+=query(L[u],L[v],Le,Ri,l,mid);
    	if(Ri > mid) ans+=query(R[u],R[v],Le,Ri,mid+1,r);
    	return ans;
    }
    
    int check(int len,int x,int a,int b){
    	int l=x,r=x;
    	for(int i=17;i>=0;i--)
    		if(l-(1<<i)>=1&&g[l][i]>=len) l-=1<<i;
    	for(int i=17;i>=0;i--)
    		if(r+(1<<i)<=n&&f[r][i]>=len) r+=1<<i;
    	int ans=query(T[l-1],T[r],a,b-len+1,1,n);
    	return ans;
    }
    
    int main()
    {
    	n=read(),m=read();
    	scanf("%s",s+1);SA(128);
    	for(int i=1;i<=n;i++) update(T[i],T[i-1],1,n,sa[i]);
    	int a,b,c,d,l,r,mid,ans=0;
    	while(m--){
    		a=read(),b=read(),c=read(),d=read();
    		l=1;r=min(b-a+1,d-c+1);ans=0;
    		while(l<=r){
    			mid=(l+r)>>1;
    			if(check(mid,rnk[c],a,b)) l=mid+1,ans=mid;
    			else r=mid-1;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    后缀自动机解法:

    因为后缀自动机只能处理后缀,所以我们将原串翻转一下,将最长前缀长度转化为最长后缀长度。然后我们对原串建一个后缀自动机,二分一下最长后缀长度 (len),从 (pos[c]) 出发倍增到 (l[x]geq len) 的结点 (x),然后在 (x) 上查询 (endpos) 集合是否有 (b+len-1..a) 的任意一个数。维护 (endpos) 集合可以用线段树合并。

    (Code Below:)

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=500000+10;
    int n,m,a[maxn],b[maxn],last,cnt,ch[maxn][26],fa[maxn],l[maxn];
    int pos[maxn],f[maxn][21],T[maxn],L[maxn*60],R[maxn*60],sum[maxn*60],tot;
    char s[maxn];
    
    inline int read(){
        register int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return (f==1)?x:-x;
    }
    
    void insert(int c){
        int p=last,q=++cnt;last=q;l[q]=l[p]+1;
        for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=q;
        if(!p) fa[q]=1;
        else {
            int r=ch[p][c];
            if(l[p]+1==l[r]) fa[q]=r;
            else {
                int s=++cnt;l[s]=l[p]+1;
                memcpy(ch[s],ch[r],sizeof(ch[r]));
                fa[s]=fa[r];fa[r]=fa[q]=s;
                for(;p&&ch[p][c]==r;p=fa[p]) ch[p][c]=s;
            }
        }
    }
    
    void pushup(int now){
        sum[now]=sum[L[now]]+sum[R[now]];
    }
    
    void update(int &now,int l,int r,int x){
        if(!now) now=++tot;
        if(l == r){sum[now]++;return ;}
        int mid=(l+r)>>1;
        if(x <= mid) update(L[now],l,mid,x);
        else update(R[now],mid+1,r,x);
        pushup(now);
    }
    
    int merge(int x,int y,int l,int r){
        if(!x||!y) return x+y;
        if(l == r){sum[x]+=sum[y];return x;}
        int mid=(l+r)>>1,z=++tot;
        L[z]=merge(L[x],L[y],l,mid);
        R[z]=merge(R[x],R[y],mid+1,r);
        pushup(z);
        return z;
    }
    
    int query(int now,int Le,int Ri,int l,int r){
        if(!now) return 0;
        if(Le <= l && r <= Ri) return sum[now];
        int mid=(l+r)>>1,ans=0;
        if(Le <= mid) ans+=query(L[now],Le,Ri,l,mid);
        if(Ri > mid) ans+=query(R[now],Le,Ri,mid+1,r);
        return ans;
    }
    
    int check(int len,int x,int L,int R){
        for(int i=20;i>=0;i--)
            if(f[x][i]&&l[f[x][i]]>=len) x=f[x][i];
        return query(T[x],L+len-1,R,1,n);
    }
    
    int main()
    {
        n=read(),m=read();
        scanf("%s",s+1);last=cnt=1;
        reverse(s+1,s+n+1);
        for(int i=1;i<=n;i++){
            insert(s[i]-'a');pos[i]=last;
            update(T[pos[i]],1,n,i);
        }
        for(int i=1;i<=cnt;i++) b[l[i]]++;
        for(int i=1;i<=cnt;i++) b[i]+=b[i-1];
        for(int i=1;i<=cnt;i++) a[b[l[i]]--]=i;
        for(int i=cnt;i>=1;i--)
            if(fa[a[i]]) T[fa[a[i]]]=merge(T[fa[a[i]]],T[a[i]],1,n);
        for(int i=1;i<=cnt;i++) f[i][0]=fa[i];
        for(int j=1;j<=20;j++)
            for(int i=1;i<=cnt;i++) f[i][j]=f[f[i][j-1]][j-1];
        int a,b,c,d,l,r,mid,ans;
        while(m--){
            a=n-read()+1,b=n-read()+1,c=n-read()+1,d=n-read()+1;
            l=0,r=min(a-b+1,c-d+1),ans=0;
            while(l<=r){
                mid=(l+r)>>1;
                if(check(mid,pos[c],b,a)) l=mid+1,ans=mid;
                else r=mid-1;
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    (转)RSS在.net下面的调用以及使用
    WEB打印的一些资料,做项目的时候用到转来(转)
    asp.net编码技巧汇集(转)也有自己的一些
    ASP.NET 中 DataGrid 导出到 Excel 的封装实现 (转)
    (转)上传与下载需要考虑的
    正则表达式之全部符号解释(转)
    开发过程遇到的无法打包问题的解决!
    传入参数,导出dbf以及删除dbf的sql 存储过程
    (转贴)MDSN关于.net连接池的讨论
    一个SessionInterPagesHelper类 (转)挺不错的想法
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/10289160.html
Copyright © 2011-2022 走看看