zoukankan      html  css  js  c++  java
  • bzoj 3998: [TJOI2015]弦论【SA+二分||SAM】

    SA的话t0直接预处理出每个后缀的不同串贡献二分即可,然后t1就按字典序枚举后缀,然后跳右端点计算和当前后缀的前缀相同的子串个数,直到第k个
    不过bzoj上会T

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=10000005;
    int n,o,sa[N],rk[N],he[N],wa[N],wb[N],wsu[N],wv[N],st[20][N],b[N];
    long long k,a[N],sm;
    char s[N];
    bool cmp(int r[],int a,int b,int l)
    {
    	return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    void saa(char r[],int n,int m)
    {
    	int *x=wa,*y=wb;
    	for(int i=0;i<=m;i++)
    		wsu[i]=0;
    	for(int i=1;i<=n;i++)
    		wsu[x[i]=r[i]]++;
    	for(int i=1;i<=m;i++)
    		wsu[i]+=wsu[i-1];
    	for(int i=n;i>=1;i--)
    		sa[wsu[x[i]]--]=i;
    	for(int j=1,p=1;j<=n&&p<n;j<<=1,m=p)
    	{
    		p=0;
    		for(int i=n-j+1;i<=n;i++)
    			y[++p]=i;
    		for(int i=1;i<=n;i++)
    			if(sa[i]>j)
    				y[++p]=sa[i]-j;
    		for(int i=1;i<=n;i++)
    			wv[i]=x[y[i]];
    		for(int i=0;i<=m;i++)
    			wsu[i]=0;
    		for(int i=1;i<=n;i++)
    			wsu[wv[i]]++;
    		for(int i=1;i<=m;i++)
    			wsu[i]+=wsu[i-1];
    		for(int i=n;i>=1;i--)
    			sa[wsu[wv[i]]--]=y[i];
    		swap(x,y);
    		p=1;
    		x[sa[1]]=1;
    		for(int i=2;i<=n;i++)
    			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p:++p;
    	}
    	for(int i=1;i<=n;i++)
    		rk[sa[i]]=i;
    	for(int i=1,j,k=0;i<=n;he[rk[i++]]=k)
    		for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
    }
    int ques(int l,int r)
    {
    	if(l>r)
    		return n-l+1;
    	int k=b[r-l+1];
    	return min(st[k][l],st[k][r-(1<<k)+1]);
    }
    int main()
    {
    	scanf("%s%d%d",s+1,&o,&k);
    	n=strlen(s+1);
    	saa(s,n,200);
    	for(int i=1;i<=n;i++)
    		a[i]=a[i-1]+n-sa[i]+1-he[i];
    	if(o==0)
    	{
    		if(a[n]<k)
    		{
    			puts("-1");
    			return 0;
    		}
    		int l=1,r=n,ans=1;
    		while(l<=r)
    		{
    			int mid=(l+r)>>1;
    			if(a[mid]>=k)
    				r=mid-1,ans=mid;
    			else
    				l=mid+1;
    		}
    		for(int i=sa[ans];i<=n-a[ans]+k;i++)
    			putchar(s[i]);
    		return 0;
    	}
    	b[0]=-1;
    	for(int i=1;i<=n;i++)
    		b[i]=b[i>>1]+1;
    	for(int i=1;i<=n;i++)
    		st[0][i]=he[i];
    	for(int i=1;i<=19;i++)
    		for(int j=1;j<=n;j++)
    			st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
    	for(int i=1;i<=n;i++)
    	{
            int nw=sa[i]+he[i],ed=n;
            while(1)
    		{//cerr<<i<<" "<<ed<<endl;
                if(nw>n) 
    				break;
                if(ed==i)
    			{
                    if(k>n-nw+1)
    				{
    					k-=n-nw+1;
    					break;
    				}
                    for(int j=sa[i];j<=nw+k-1;j++) 
    					putchar(s[j]);
                    return 0;
                }
    			int l=i,r=ed;
                while(l<=r)
    			{
                    int mid=(l+r)>>1;
                    if(ques(i+1,mid)>=nw-sa[i]+1) 
    					l=mid+1,ed=mid;
                    else 
    					r=mid-1;
                }
                if(k>ed-i+1) 
    				k-=ed-i+1;
                else
    			{
                    for(int j=sa[i];j<=nw;j++) 
    					putchar(s[j]);
                    return 0;
                }
    			nw++;
            }
        }
    	puts("-1");
        return 0;
    }
    

    SAM的话就利用trie树的性质,t0就每个点size=1,t1就每个点计算一下parent树上这个点下面有几个后缀终止点
    然后计算trie树上的子树size和,枚举转移字符直到第k个即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=1000006;
    int n,o,k,fa[N],ch[N][26],tot=1,cur=1,la,dis[N],wsu[N],sa[N];
    long long si[N],sm[N],ans;
    char s[N];
    void ins(int c,int id)
    {
    	la=cur,dis[cur=++tot]=id;
    	si[cur]=1;
    	int p=la;
    	for(;p&&!ch[p][c];p=fa[p])
    		ch[p][c]=cur;
    	if(!p)
    		fa[cur]=1;
    	else
    	{
    		int q=ch[p][c];
    		if(dis[q]==dis[p]+1)
    			fa[cur]=q;
    		else
    		{
    			int nq=++tot;
    			dis[nq]=dis[p]+1;
    			memcpy(ch[nq],ch[q],sizeof(ch[q]));
    			fa[nq]=fa[q];
    			fa[q]=fa[cur]=nq;
    			for(;ch[p][c]==q;p=fa[p])
    				ch[p][c]=nq;
    		}
    	}
    }
    int main()
    {
    	scanf("%s%d%d",s+1,&o,&k);
    	n=strlen(s+1);
    	for(int i=1;i<=n;i++)
    		ins(s[i]-'a',i);
    	for(int i=1;i<=tot;i++)
    		wsu[dis[i]]++;
    	for(int i=1;i<=n;i++)
    		wsu[i]+=wsu[i-1];
    	for(int i=tot;i>=1;i--)
    		sa[wsu[dis[i]]--]=i;
    	for(int i=tot;i>=1;i--)
    		o?si[fa[sa[i]]]+=si[sa[i]]:si[sa[i]]=1;
    	si[1]=0;
    	for(int i=tot;i>=1;i--)
    	{
    		sm[sa[i]]=si[sa[i]];
    		for(int j=0;j<26;j++)
    			if(ch[sa[i]][j])
    				sm[sa[i]]+=sm[ch[sa[i]][j]];
    	}
    	if(sm[1]<k)
    	{
    		puts("-1");
    		return 0;
    	}
    	int nw=1;
    	while((k-=si[nw])>0)
    	{
    		int w;
    		for(int i=0;i<26;i++)
    			if(ch[nw][i])
    			{
    				if(sm[ch[nw][i]]<k)
    					k-=sm[ch[nw][i]];
    				else
    				{
    					w=i;
    					break;
    				}
    			}
    		putchar(w+'a');
    		nw=ch[nw][w];
    	}
    	return 0;
    }
    
  • 相关阅读:
    JAVA面向对象继承 及super的学习
    JAVA 封装的学习
    submit和button的区别
    Servlet四大域对象
    转发和重定向的区别
    JSTL标签用法 详解
    JSP中EL表达式的使用
    解决idea的项目启动报404的问题
    intellij idea 创建动态web项目
    解决mysql数据库中文乱码问题
  • 原文地址:https://www.cnblogs.com/lokiii/p/10718981.html
Copyright © 2011-2022 走看看