题解
字符串果然还是我的弱项。。。还得继续刷后缀数组和AC自动机
在讨论这道题之前,先来想一下后缀数组有什么用
1、后缀排序
2、O(nlogn)预处理ST表,O(1)求LCP
好像没有什么了吧。。。
其实它的用处还有很多很多:https://blog.csdn.net/c20181220_xiang_m_y/article/details/104017226
这里用到了它的一个用处:把所有本质不同的子串进行排序
怎么做?
把排序好的后缀数组拿出来,从小到大枚举排名 i ,以sa[i]作为左端点,j从sa[i]+height[i]枚举到n,以 j 作为右端点
然后发现这些子串都是本质不同且字典序递增的(其实想想就知道为什么了)
于是我们就可以来对n-(sa[i]+height[i])+1做一个前缀和,就可以用lower_bound来查出一个排名对应的子串啦
这道题要求的就是所有划分方案中,对于一个划分方案,求出它划分后的所有串的最大子串的最大串,然后求所有方案的答案的最小串。最小化最大值,可以联想到二分
我们发现对于一个串,如果要成为答案,那么所有字典序大于它的串必须被划分到多个段
当我们二分了一个串,可以考虑倒序贪心,当遇到一个整串的字典序大于二分的答案,我们就把它与它的第一个字符划分成两段
(为什么是倒序,因为固定了一个右端点,左端点l到右段点r中的所有前缀的最大值就是l~r的字符串)
这里还要用到后缀数组的另一个用途:O(1)比较任意两个子串的字典序(详见代码)
代码:٩(๑>◡<๑)۶
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
#define LOG 18
#define LL long long
int n,m;char a[N];
int *sa,*rk,*nsa,*nrk,arr[4][N],b[N],h[N];
int f[N][LOG+2],lg[N];
LL sum[N];
void SA()
{
sa=arr[0];rk=arr[1];nsa=arr[2];nrk=arr[3];
int i,j,k,mx=0;
for(i=1;i<=n;i++)b[(int)a[i]]++;
for(i=1;i<=200;i++)b[i]+=b[i-1];
for(i=1;i<=n;i++)sa[b[(int)a[i]]--]=i;
for(i=1;i<=n;i++)rk[sa[i]]=rk[sa[i-1]]+(a[sa[i]]!=a[sa[i-1]]);
for(k=1;k<=n&&rk[sa[n]]<n;k<<=1){
for(i=1;i<=n;i++)b[rk[sa[i]]]=i;
for(i=n;i>=1;i--)if(sa[i]>k)nsa[b[rk[sa[i]-k]]--]=sa[i]-k;// !
for(i=n-k+1;i<=n;i++)nsa[b[rk[i]]--]=i;// !
for(i=1;i<=n;i++)nrk[nsa[i]]=nrk[nsa[i-1]]+(rk[nsa[i]]!=rk[nsa[i-1]]||rk[nsa[i]+k]!=rk[nsa[i-1]+k]);
swap(sa,nsa);swap(rk,nrk);
}
for(i=1,k=0;i<=n;i++){
if(rk[i]==1)h[1]=0;
else{
if(k)k--;
for(j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
h[rk[i]]=k;
}
}
for(i=1;i<=n;i++){
f[i][0]=h[i];// !
lg[i]=lg[i>>1]+1;
mx=max(mx,(int)a[i]);
}
for(j=1;j<=LOG;j++)
for(i=1;i<=n;i++)if(i+(1<<(j-1))<=n)// !
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(i=1;i<=n;i++){
if((int)a[sa[i]]==mx)sum[i]+=n-(sa[i]+h[i])+1;
sum[i]+=sum[i-1];
}
}
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);x++;
int p=lg[y-x+1]-1;
return min(f[x][p],f[y-(1<<p)+1][p]);
}
inline bool cmp(int l,int r,int x,int y)
{
int len=min(LCP(l,x),min(r-l+1,y-x+1));
if(len<r-l+1&&len<y-x+1)return a[l+len]<a[x+len];
if(len==r-l+1&&len<y-x+1)return 1;
return 0;
}
void get(int &l,int &r,LL k)
{
int p=lower_bound(sum+1,sum+n+1,k)-sum;
l=sa[p];r=sa[p]+h[p]+k-sum[p-1]-1;
}
int check(LL k)
{
int l,r,i,j,ret=0;
get(l,r,k);
for(i=n;i>=1;i=j){
j=i-1;ret++;
while(j>=1&&!cmp(l,r,j,i))j--;
}
return ret;
}
int main()
{
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
int i,pl,pr;
LL l,r,mid;
scanf("%d%s",&m,a+1);
n=strlen(a+1);SA();
l=1;r=sum[n];
while(l<r){
mid=(l+r)>>1;
if(check(mid)>m)
l=mid+1;
else
r=mid;
}
get(pl,pr,l);
for(i=pl;i<=pr;i++)
printf("%c",a[i]);
}
调了我2h。。。最后发现后缀数组的模板打错了,身败名裂啊啊啊啊。。。。。
来源:CSDN
作者:cqbzcsq
链接:https://blog.csdn.net/C20180602_csq/article/details/104111502