20200129模拟赛T1 string

题目描述:

分析:

看到题目:后缀数组,二分,加加减减
然后。。。然后。。。

“后缀数组是个好东西,我有头发的时候天天写。”

我们首先要求出二分的区间,可能二分到所有子串字典序编号
二分的过程当中,首先先找出字典序当前值得字符串,这里要用到height数组RMQ
我们现在就需要将所有字典序大于该子串的切掉,从前往后枚举开头i,当LCP大于目标串的Len时,说明字典序更大,这时候要将i+Len位置切掉
最后通过切的次数来判断二分

以上说得很好。。。
真就写20分钟,调两个小时

“后缀数组是个好东西,我有头发的时候天天写。”

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

#define maxn 200005

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,k;
char s[maxn];
int st,len;
int sa[maxn],height[maxn];
int rk[maxn],bit[maxn],f[21][maxn];
int cnt[maxn],x[maxn],y[maxn];
int aa[maxn];

inline void suffix()
{
	memset(cnt,0,sizeof cnt);
	for(int i=1;i<=n;i++)cnt[int(s[i])]++;
	for(int i=1;i<=128;i++)cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;i--)sa[cnt[int(s[i])]--]=i;
	rk[sa[1]]=1;
	for(int i=2;i<=n;i++)rk[sa[i]]=rk[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
	for(int k=1;rk[sa[n]]!=n;k<<=1)
	{
		for(int i=1;i<=n;i++) 
		{
			x[i]=rk[i];
			y[i]=(i+k<=n)?rk[i+k]:0;
		}
		memset(cnt,0,sizeof cnt);
		for(int i=1;i<=n;i++)cnt[y[i]]++;
		for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--)rk[cnt[y[i]]--]=i;
		memset(cnt,0,sizeof cnt);
		for(int i=1;i<=n;i++)cnt[x[i]]++;
		for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--)sa[cnt[x[rk[i]]]--]=rk[i];
		rk[sa[1]]=1;
		for(int i=2;i<=n;i++)rk[sa[i]]=rk[sa[i-1]]+(x[sa[i]]!=x[sa[i-1]]||y[sa[i]]!=y[sa[i-1]]);
	}
	int now=0;
	for(int i=1;i<=n;i++)
	{
		if(now)now--;
		for(int j=sa[rk[i]+1];s[j+now]==s[i+now];now++);
		height[rk[i]]=now;
	}
	for(int i=1;i<=n;i++)
	{
		f[0][i]=height[i];
		bit[i]=bit[i-1];
		if(i>=(1<<(bit[i]+1)))bit[i]++;
	}
	for(int p=1;p<20;p++)
		for(int i=1,j=(1<<(p-1))+1;j<=n;i++,j++)
			f[p][i]=min(f[p-1][i],f[p-1][j]);
	for(int i=1;i<=n;i++)aa[i]=rk[i];
}

inline int LCP(int x,int y)
{
	if(x==y)return n-x+1;
	x=rk[x],y=rk[y];
	if(x>y)swap(x,y);
	int tmp=bit[y-x];
	return min(f[tmp][x],f[tmp][y-(1<<tmp)]);
}

inline long long getl()
{
	long long ans=1;
	for(int i=1;s[sa[i]]!=s[sa[n]];i++)
		ans+=n-sa[i]+1-height[i];
	return ans;
}

inline long long getr()
{
	long long ans=0;
	for(int i=1;i<=n;i++)
		ans+=n-sa[i]+1-height[i];
	return ans;
}

inline void solve(long long num)
{
	int pos;
	for(pos=1;n-sa[pos]+1<num;pos++)
		num-=n-sa[pos]+1-height[pos];
	st=sa[pos];len=num;
}

inline bool check(int k)
{
	if(len==1)return 0;
	int ans=0,ed=n;
	for(int i=1;i<=n;i++)
	{
		int tmp=LCP(i,st);
		if(tmp>=len)ed=min(ed,i+len-2);
		else if(s[i+tmp]>s[st+tmp])ed=min(ed,i+tmp-1);
		if(i==ed)ans++,ed=n;
	}
	return ans<=k;
}


int main()
{
	k=getint();
	scanf("%s",s+1);
	n=strlen(s+1);
	suffix();
	for(int i=1;i<=n;i++)rk[i]=aa[i];
	long long l=getl(),r=getr();
	while(l<r)
	{
		long long mid=(l+r+1)/2;
		solve(mid);
		if(check(k))r=mid-1;
		else l=mid;
	}
	solve(l);
	s[st+len]=0;
	printf("%s\n",s+st);
}

posted @ 2020-01-30 19:46  Izayoi_Doyo  阅读(149)  评论(0编辑  收藏  举报