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

    传送门

    解法一:后缀数组

    T=0,就是求本质不同,后缀数组经典用法
    T=1,二分解决
    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    void read(int &x) {
    	char ch; bool ok;
    	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
    }
    #define rg register
    const int maxn=5e5+10;
    int n,k,num,x[maxn],a[maxn],y[maxn],sa[maxn],rk[maxn],h[maxn],opt,m='z',sum[maxn];char p[maxn];
    int main()
    {
    	scanf("%s",p+1);read(opt),read(k);n=strlen(p+1);
    	for(rg int i=1;i<=n;i++)a[x[i]=p[i]]++;
    	for(rg int i=1;i<=m;i++)a[i]+=a[i-1];
    	for(rg int i=n;i;i--)sa[a[x[i]]--]=i;
    	for(rg int k=1;k<=n;k<<=1,num=0)
    	{
    		for(rg int i=n-k+1;i<=n;i++)y[++num]=i;
    		for(rg int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
    		for(rg int i=1;i<=m;i++)a[i]=0;
    		for(rg int i=1;i<=n;i++)a[x[i]]++;
    		for(rg int i=1;i<=m;i++)a[i]+=a[i-1];
    		for(rg int i=n;i;i--)sa[a[x[y[i]]]--]=y[i];
    		for(rg int i=1;i<=n;i++)y[i]=x[i];
    		num=x[sa[1]]=1;
    		for(rg int i=2;i<=n;i++)
    			if(y[sa[i]]!=y[sa[i-1]]||y[sa[i]+k]!=y[sa[i-1]+k])x[sa[i]]=++num;
    			else x[sa[i]]=num;
    		if(num>=n)break;m=num;
    	}
    	for(rg int i=1;i<=n;i++)rk[sa[i]]=i;
    	for(rg int i=1,j,k=0;i<=n;h[rk[i++]]=k)
    		for(k=k?k-1:k,j=sa[rk[i]-1];p[j+k]==p[i+k];k++);
    	for(rg int i=1;i<=n;i++)sum[i]=sum[i-1]+n-sa[i]+1;
    	if(!opt)
    	{
    		for(rg int i=1;i<=n;i++)
    			if(n-sa[i]+1<k)k=k-(n-sa[i]+1)+h[i+1];
    			else 
    			{
    				for(rg int j=sa[i];j<=sa[i]+k-1;j++)printf("%c",p[j]);printf("
    ");
    				return 0;
    			}
    	}
    	else
    	{
    		int L=1,R=n;
            for(int i=1;i<=n;i++)
    		{
                int tmp=L;
                for(int j='a';j<='z';j++)
    			{
                    int l=tmp,r=R;
                    while(l<=r)
    				{
                        int mid=l+r>>1;
                        if(p[sa[mid]+i-1]>j)r=mid-1;
                        else l=mid+1;
                    }
                    long long t=sum[r]-sum[tmp-1]-1LL*(r-tmp+1)*(i-1);
                    if(k<=r-tmp+1)
    				{
                        for(int j=sa[tmp];j<=sa[tmp]+i-1;j++)printf("%c",p[j]);
                        return 0;
                    }
                    if(t>=k)
    				{
                        L=tmp,R=r;
                        k-=r-tmp+1;
                        break;
                    }
                    tmp=r+1,k-=t;
                }
                if(n-sa[L]+1==i)L++;
            }
        }
    	printf("-1
    ");
    }
    

    解法二:后缀自动机

    后缀数组做这个题显然有些强人所难
    所以我们用后缀自动机可以更简单的解决
    我们知道后缀自动机可以(O(n))求出每个子串的出现次数和不同字串的个数
    将这两者同意一下就可以满足这个题目的要求了
    至于求第(k)小的,直接在后缀自动机上搜索,但是爆搜是不行的,显然会TLE
    考虑我们已经求出了每个子串的出现次数,我们就可以利用这个来优化搜索了,复杂度(O(n))
    基数排列的时候要从1开始,从0开始会有问题,需要处理一下
    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    void read(int &x){
    	char ch; bool ok;
    	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
    }
    #define rg register
    const int maxn=5e6+10;
    int n,tot,ans,las,sum,k,g[maxn],t[maxn],id[maxn];
    int flag,cnt,f[maxn],m;
    char a[maxn],b[maxn];
    struct sam{int len,link,ch[26];}s[maxn];
    void sam_pre(){s[++tot].len=0,s[tot].link=-1,las=tot;}
    void ins(int x){
    	int cur=++tot,p=las;s[cur].len=s[p].len+1;f[tot]=1;
    	while(p!=-1&&!s[p].ch[x])s[p].ch[x]=cur,p=s[p].link;
    	if(p==-1)s[cur].link=1;
    	else{
    		int q=s[p].ch[x];
    		if(s[q].len==s[p].len+1)s[cur].link=q;
    		else{
    			int now=++tot;s[now].len=s[p].len+1;
    			s[now].link=s[q].link;
    			memcpy(s[now].ch,s[q].ch,sizeof s[q].ch);
    			while(p!=-1&&s[p].ch[x]==q)s[p].ch[x]=now,p=s[p].link;
    			s[q].link=s[cur].link=now;
    		}
    	}
    	las=cur;
    }
    void solve(int x,int k){
    	if(k<=f[x])return ;
    	k-=f[x];
    	for(rg int i=0;i<26;i++)
    		if(s[x].ch[i]){
    			if(k>=g[s[x].ch[i]]){k-=g[s[x].ch[i]];continue;}
    			putchar(i+'a'),solve(s[x].ch[i],k);return ;
    		}
    }
    int main(){
    	scanf("%s",a+1),read(m),read(k),n=strlen(a+1),sam_pre();
    	for(rg int i=1;i<=n;i++)ins(a[i]-'a');
    	for(rg int i=1;i<=tot;i++)t[s[i].len]++;
    	for(rg int i=1;i<=tot;i++)t[i]+=t[i-1];
    	for(rg int i=1;i<=tot;i++)id[t[s[i].len]--]=i;
    	for(rg int i=tot;i>=1;i--)f[s[id[i]].link]+=f[id[i]];
    	for(rg int i=1;i<=tot;i++)(m?(g[i]=f[i]):(g[i]=f[i]=1));
    	g[1]=f[1]=0;
    	for(rg int i=tot;i>=1;i--)
    		for(rg int j=0;j<26;j++)
    			if(s[id[i]].ch[j])g[id[i]]+=g[s[id[i]].ch[j]];
    	if(g[1]<k)printf("-1
    ");
    	else solve(1,k);
    }
    
  • 相关阅读:
    WebAPI实现移动端上传头像接口
    WebAPI发布IIS报错问题
    EF 解除属性映射到数据库中 NotMappedAttribute无效解决办法
    更改MVC注册Areas的顺序,掌控Areas的运作
    js判断字符串是否为JSON格式
    docker commit命令
    强大的strace命令用法详解
    Openshift概念
    如何在生产环境使用Btrace进行调试
    kill-9 kill-15
  • 原文地址:https://www.cnblogs.com/lcxer/p/10472794.html
Copyright © 2011-2022 走看看