Codeforces 128B string(后缀数组)
链接
题目大意:
给一个长度为n的字符串,求出这个字符串的所有子串(允许存在相同的子串)的字典序第k小是哪一个。(n<=1e5,k<=1e5)
题解:
因为每个后缀的前缀就是一个子串,先对这个串求后缀数组,维护当前子串排名nowrk,然后由rank从小到大枚举所有后缀,对于每一个后缀,可以从与前一个后缀的最长公共前缀+1开始暴力遍历它的前缀,求出后面的后缀与当前后缀的前缀的公共前缀的个数m(求m可以先预处理高度数组的rmq,根据height数组的性质二分),那么当前这个前缀代表的子串的排名就是nowrk+m+1。因为k<=1e5所以最多求1e5次就能得到答案复杂度O(nlogn)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=100005;
int sa[maxn],rk[maxn],fir[maxn],sec[maxn],tmp[maxn],buckt[maxn];
int a[maxn],b[maxn],height[maxn];
char s[maxn];
void SA(int n)
{
for(int i = 0;i < n;++i)
a[i+1]=b[i+1]=s[i];
sort(b+1,b+1+n);
int cnt=unique(b+1,b+1+n)-b-1;
for(int i = 1;i <= n;++i)
a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
for(int i = 1;i <= n;++i)buckt[a[i]]++;
for(int i = 1;i <= n;++i)buckt[i]+=buckt[i-1];
for(int i = 1;i <= n;++i)rk[i]=buckt[a[i]-1]+1;
for(int t = 1;t <= n;t<<=1)
{
for(int i = 1;i <= n;++i)fir[i]=rk[i];
for(int i = 1;i <= n;++i)sec[i]=(i+t>n?0:rk[i+t]);
memset(buckt,0,sizeof(buckt));
for(int i = 1;i <= n;++i)buckt[sec[i]]++;
for(int i = 1;i <= n;++i)buckt[i]+=buckt[i-1];
for(int i = 1;i <= n;++i)tmp[n-(--buckt[sec[i]])]=i;
memset(buckt,0,sizeof(buckt));
for(int i = 1;i <= n;++i)buckt[fir[i]]++;
for(int i = 1;i <= n;++i)buckt[i]+=buckt[i-1];
for(int j = 1,i;j <= n;++j)i = tmp[j],sa[buckt[fir[i]]--]=i;
bool ok=true;
for(int i = 1,j,last=0;i <= n;++i)
{
j = sa[i];
if(!last)
rk[j]=1;
else if(fir[j]==fir[last]&&sec[j]==sec[last])
rk[j]=rk[last],ok=false;
else
rk[j]=rk[last]+1;
last=j;
}
if(ok)
break;
}
for(int i = 1;i <= n;++i)rk[sa[i]]=i;
for(int i = 1,k=0,j;i <= n;++i)
{
if(rk[i]==1)
k=0;
else
{
if(k)
k--;
j = sa[rk[i]-1];
while(a[j+k]==a[i+k]&&j+k<=n&&i+k<=n)k++;
}
height[rk[i]]=k;
}
}
int mn[20][maxn];
int Log[maxn];
int bin[20];
void ST(int n){
bin[0]=1;
for(int i=1;i<20;i++)
bin[i]=bin[i-1]*2;//bin[i]表示2的i次方
Log[0]=-1;
for(int i=1;i<=100000;i++)
Log[i]=Log[i/2]+1;//Log[i]表示log(i)
for(int i=1;i<=n;i++)
mn[0][i]=height[i];//显然i到i+2^0-1就i一个位置,那么最小值等于自己本身的值
for(int i=1;i<=Log[n];i++)
for(int j=1;j<=n;j++)
if(j+bin[i]-1<=n)
mn[i][j]=min(mn[i-1][j],mn[i-1][j+bin[i-1]]);//状态继承
}
int query(int x,int y)
{
int t=Log[y-x+1];
return min(mn[t][x],mn[t][y-bin[t]+1]);
}
int main()
{
scanf("%s",s);
int k;
scanf("%d",&k);
int n=strlen(s);
SA(n);
ST(n);
int nowrk = 0;
string ans;
for(int i = 1;i <= n;++i)
{
int st = height[i]+1;
for(int j = st;j <= n-sa[i]+1;++j)
{
int l = i+1,r = n;
while(l<=r)
{
int mid=l+r>>1;
if(query(i+1,mid)>=j)
l = mid+1;
else
r = mid-1;
}
int tmp = l-1;
// printf("%d\n",tmp);
// system("pause");
nowrk+=tmp-i+1;
if(nowrk>=k)
{
for(int kk = sa[i],p = 0;p < j;++p)
ans+=s[kk+p-1];
cout<<ans<<endl;
return 0;
}
}
}
if(ans.size()>0)
cout<<ans<<endl;
else
cout<<"No such line."<<endl;
return 0;
}