题目
不同位置相同子串算一个,以及不同位置相同子串算多个,求第K小的子串
题解
SA好题啊! 考场写挂。。。
前一个问非常简单
每个后缀有
n
−
s
a
i
−
h
i
+
1
n-sa_i-h_i+1
n−sai−hi+1个子串贡献
后一个问思考二分前一问的排名
令前一问排名为
m
i
d
mid
mid的字符串的起始位在第
p
o
s
pos
pos名后缀处
算验证比
m
i
d
mid
mid小的个数是否大于
k
k
k
计算比
m
i
d
mid
mid小的个数:
∑
i
=
1
p
o
s
−
1
−
s
a
i
+
1
+
∑
i
=
p
o
s
n
m
i
n
(
l
c
p
(
p
o
s
,
i
)
,
w
)
\sum_{i=1}^{pos-1}-sa_i+1+\sum_{i=pos}^{n}min(lcp(pos,i),w)
∑i=1pos−1−sai+1+∑i=posnmin(lcp(pos,i),w)
l
c
p
lcp
lcp用ST表O(1)即可
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,op;
char s[N];
int rk[N],sa[N],tp[N],t[N];
void Qsort()
{
for(int i=1;i<=m;i++)t[i]=0;
for(int i=1;i<=n;i++)t[rk[i]]++;
for(int i=1;i<=m;i++)t[i]+=t[i-1];
for(int i=n;i>=1;i--)sa[t[rk[tp[i]]]--]=tp[i];
}
void SA()
{
for(int i=1;i<=n;i++)rk[i]=s[i]-'a'+1,tp[i]=i;m=30;Qsort();
for(int w=1,p;w<=n;m=p,w<<=1)
{
p=0;
for(int i=n-w+1;i<=n;i++)tp[++p]=i;
for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;
Qsort();swap(tp,rk);p=rk[sa[1]]=1;
for(int i=2;i<=n;i++)rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])?p:++p;
if(p==n)return ;
}
}
int h[N];
void get_h()
{
int k=0;
for(int i=1;i<=n;i++)
{
if(rk[i]==1)continue;
int j=sa[rk[i]-1];if(k)k--;
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
h[rk[i]]=k;
}
}
int bz[N][20];
void ST()
{
for(int i=1;i<=n;i++)bz[i][0]=h[i];
for(int j=1;j<=20;j++)
for(int i=1;i<=n-(1<<j)+1;i++)
bz[i][j]=min(bz[i][j-1],bz[i+(1<<(j-1))][j-1]);
}
int minn(int l,int r)
{
if(l!=r)l++;
else return n-sa[l]+1;
int w=log2(r-l+1);
return min(bz[l][w],bz[r-(1<<w)+1][w]);
}
//--
void get(int k,int &st,int &len)
{
st=0;
for(int i=1;i<=n;i++)
{
int w=n-sa[i]-h[i]+1;
if(k<=w){st=i;len=k;return ;}
k-=w;
}
}
int k;
int st,len;
bool check(int mid)
{
get(mid,st,len);
int tmp=0;
for(int i=1;i<st;i++)tmp+=n-sa[i]+1;
for(int i=st;i<=n;i++)tmp+=min(len,minn(st,i));
return tmp>=k;
}
int main()
{
scanf("%s%d%d",s+1,&op,&k);
n=strlen(s+1);
SA();get_h();ST();
if(op==0)
{
get(k,st,len);
if(st)for(int i=sa[st];i<sa[st]+len+h[st];i++)printf("%c",s[i]);
else printf("-1");
}
else
{
if(k>1ll*n*(n+1)/2){printf("-1");return 0;}
int l=0,r=k+1,ans=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}
get(ans,st,len);
for(int i=sa[st];i<sa[st]+len;i++)printf("%c",s[i]);
}
}
其实此题原本是SAM板题,但作者有一点心理阴影,就等到什么时候有空再补SAM做法 咕掉吧
Q
w
Q
QwQ
QwQ