zoukankan      html  css  js  c++  java
  • BZOJ3998[TJOI2015]弦论

    Description

    对于一个给定长度为$N$的字符串,求它的第$K$小子串是什么。

    [1]不同位置的相同子串算作$1$个;
    [2]不同位置的相同子串算作多个;

    Input

    第一行是一个仅由小写英文字母构成的字符串$S$

    第二行为两个整数$T$和$K$,$T$为$0$则表示不同位置的相同子串算作一个。$T=1$则表示不同位置的相同子串算作多个。$K$的意义如题所述。

    Output

    输出仅一行,为一个数字串,为第$K$小的子串。如果子串数目不足$K$个,则输出$-1$
     
     
    题解
    模拟赛里写的时候本蒟蒻并不会后缀自动机的做法,然而后缀数组的方法我还是会的...
    雅礼集训水箱一题和2018洛谷7月月赛beautiful pair这两题的类似分治一样的方法的总结,这种思路对我而言还是很好想到并实现的。
     
    首先建出后缀数组...
     
    然后,按排名找到两两之间最小的$height$值,如果相同取排名更靠前的,这样也就找出了最长的每一个后缀都有的一个相同的前缀$S$。由于后缀数组的性质,这个$S$的每一个前缀一定恰好是所有子串中字典序最小的那一部分字符串。这时候这一做法比后缀自动机更优的地方就体现出来了,对于$T=0$和$T=1$的差异,唯一的差别是,当$T=0$时,$S$的每一个前缀都对答案有$1$的贡献,当$T=1$时,$S$的每一个前缀都对答案有$N$的贡献。如果我们发现这些$S$的前缀不足以成为第$K$大的子串,那么就将$K$减去所有这些$S$的前缀的贡献之和,再优先递归处理排名更靠前的区间,接着再处理另一个区间。
     
    注意,递归时要新增一参数$last$,表示上一级处理出的$S$为$S'$,因为当我们处理出当前区间的$S$时,有意义的部分只有每一个长度大于$|S'|$的$S$的前缀,因为$S'$的前缀已经在上一级被统计过了。
     
    复杂度证明:
    和水箱那题的方法如出一辙,不过为了保持新鲜感,还是换个方法。
    后缀数组本身是$O(Ncdot log(N))$,使用线段树维护$height$最小值出现的位置,每次查询为$O(log(N))$,一共查询的次数是长度大于一的区间,每个区间由两个子区间构成。想象一下,初始有$N$个长度为$1$的区间,每次选出两个合并,产生一个新的长度大于$1$的区间,同时总的区间的数量$-1$,这样一共会产生$N-1$个长度大于$1$的区间。而一定有一种合并的方案,使得每次产生出的区间恰好对应每一个要查询的区间,这样的区间恰好有$N-1$个,所以复杂度$O(Ncdot log(N))$。
    于是总复杂度$O(Ncdot log(N))$。
     
    然而模拟赛中,写$init$函数和$solve$函数的顺序看着别扭,全选了一个函数在Dev C++ 5.1.1中用$Ctrl+Shift+uparrow downarrow$进行整体上移下移,随后又瞎按了几下$Ctrl+Z$(撤销)和$Ctrl+Y$(重做)导致后缀数组的某一个部分出现了两遍最后交上去极其惨烈......
     
    AC代码如下
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define mid (l+r>>1) 
    #define M 500020
    using namespace std;
    int read(){
    	int nm=0,fh=1;char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    int n,m=124,rk[M],sa[M],pos[M],c[M],height[M],T,K;
    int p[M<<2];
    char ch[M];
    void finish(int l,int r){
    	for(int i=l;i<=r;i++) putchar(ch[i]);
    	putchar('
    '); exit(0);
    }
    void build(int x,	int l,int r){
    	if(l==r){p[x]=l;return;}
    	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
    	int t1=p[x<<1],t2=p[x<<1|1];
    	if(height[t1]<=height[t2]) p[x]=t1;
    	else p[x]=t2;
    }
    int query(int x,int l,int r,int L,int R){
    	if(L<=l&&r<=R) return p[x];
    	if(R<l||r<L) return 1;
    	int t1=query(x<<1,l,mid,L,R);
    	int t2=query(x<<1|1,mid+1,r,L,R);
    	if(height[t1]<=height[t2]) return t1;
    	else return t2;
    }
    void init(){
    	scanf("%s",ch+1),n=strlen(ch+1),T=read(),K=read();
    	for(int i=1;i<=n;i++) c[rk[i]=ch[i]]++;
    	for(int i=2;i<=m;i++) c[i]+=c[i-1];
    	for(int i=n;i>=1;i--) sa[c[rk[i]]--]=i;
    	for(int k=1;k<=n;k<<=1){
    		int tot=0;
    		for(int i=n-k+1;i<=n;i++) pos[++tot]=i;
    		for(int i=1;i<=n;i++) if(sa[i]>k) pos[++tot]=sa[i]-k; 
    		for(int i=1;i<=m;i++) c[i]=0;
    		for(int i=1;i<=n;i++) c[rk[i]]++;
    		for(int i=2;i<=m;i++) c[i]+=c[i-1];
    		for(int i=n;i>=1;i--) sa[c[rk[pos[i]]]--]=pos[i];
    		swap(pos,rk),tot=rk[sa[1]]=1;
    		for(int i=2;i<=n;i++) rk[sa[i]]=(pos[sa[i]]==pos[sa[i-1]]&&pos[sa[i]+k]==pos[sa[i-1]+k]?tot:++tot);
    		if((m=tot)==n) break;
    	}
    	for(int i=1,k=0;i<=n;i++){
    		if(k) k--;
    		if(rk[i]==1) continue;
    		for(int j=sa[rk[i]-1];i+k<=n&&j+k<=n&&ch[j+k]==ch[i+k];k++);
    		height[rk[i]]=k;
    	}
    	height[1]=n,build(1,1,n);
    }
    void solve(int l,int r,int last){
    	if(l==r){
    		if(K<=n-sa[l]+1-last) finish(sa[l],sa[l]+last+K-1);
    		else{K-=n-sa[l]+1-last;return;}
    	}
    	int now=query(1,1,n,l+1,r),res=T==0?1:r-l+1;
    	int dt=res*(height[now]-last);
    	if(K>dt&&dt>=0) K-=dt,solve(l,now-1,height[now]),solve(now,r,height[now]);
    	else{for(K>res;K-=res,last++;); finish(sa[r],sa[r]+last);}
    }
    int main(){init(),solve(1,n,0),puts("-1"); return 0;}
  • 相关阅读:
    Bootstrapbutton组
    Hadoop
    图像手工画效果【QT+OpenCV】
    经常使用传感器协议1:CJ/T-188 水表协议解析1
    神经网络的初识
    用队列实现栈
    sas数据导入终极汇总-之中的一个
    SPOJ 题目705 New Distinct Substrings(后缀数组,求不同的子串个数)
    怎样选择正确的HTTP状态码
    最新最全的iOS手机支付总结
  • 原文地址:https://www.cnblogs.com/OYJason/p/9470960.html
Copyright © 2011-2022 走看看