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);
}
原文地址:https://www.cnblogs.com/lcxer/p/10472794.html