zoukankan      html  css  js  c++  java
  • 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
    ",s+st);
    }
    

  • 相关阅读:
    【转】Lesktop 1.0 ——开源JS控件库,WebIM和网盘
    个人开公司的流程,以后用得着(转)
    C#中的弱引用
    同步异步的知识补充
    SynchronizationContext
    泛型弱引用(不继承 System.WeakReference)
    【转】亂數產生器:Random 與 RNGCryptoServiceProvider
    Oracle trunc()函数的用法
    [转]C# WinForm自定义控件整理
    自定义控件小结进阶篇(转)
  • 原文地址:https://www.cnblogs.com/Darknesses/p/12243581.html
Copyright © 2011-2022 走看看