P3975 (后缀自动机sort)
题目链接:
https://www.luogu.org/problem/P3975
题意:
求出所有字串的第k大子串
有两种,第一种对于出现在不同位置的相同子串算作一个子串
第二种,对于不同位置的子串算作是不相同子串
数据范围:
$1\leq |S| \leq5000 00$
分析:
对于第一种计算,endpos算作1,对于第二种,endpos是状态出现的次数
对于当前状态前缀出现的次数为endpos+子状态的出现次数
sam的拓扑序可以根据maxlen来排,maxlen越大,拓扑序肯定越小,无论是slink,还是trans边,都是短的maxlen链接长的maxlen
用桶排序实现$O(n)$为它们排序
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long ll;
char S[N/2];
char ans[N/2];
int fla,k,top;
struct suffixautomation
{
int mp[N][30];int fa[N];int ed;int ct;int len[N];int siz[N];
ll weight[N];
int z[N],A[N];
suffixautomation(){ed=ct=1;}
inline void ins(int c,int pos)
{
int p=ed;siz[ed=++ct]=1;len[ed]=pos;//先初始化size和len
for(;p&&mp[p][c]==0;p=fa[p]){mp[p][c]=ed;}//然后顺着parent树的路径向上找
if(p==0){fa[ed]=1;return;}int q=mp[p][c];//case1
if(len[p]+1==len[q]){fa[ed]=q;return;}//case2
len[++ct]=len[p]+1;//case 3
for(int i=1;i<=26;i++){mp[ct][i]=mp[q][i];}
fa[ct]=fa[q];fa[q]=ct;fa[ed]=ct;
for(int i=p;mp[i][c]==q;i=fa[i]){mp[i][c]=ct;}
}
void solve(){
//sort
for(int i=1;i<=ct;i++)z[len[i]]++;
for(int i=1;i<=ct;i++)z[i]+=z[i-1];
for(int i=1;i<=ct;i++)A[z[len[i]]--]=i;
for(int i=ct;i>=1;i--){
if(fla)siz[fa[A[i]]]+=siz[A[i]];
else siz[fa[A[i]]]=1;
}
for(int i=ct;i>=1;i--){
// cout<<A[i]<<endl;
int v=A[i];
weight[v]=siz[v];
for(int j=1;j<=26;j++)
if(mp[v][j])weight[v]+=weight[mp[v][j]];
}
weight[1]-=siz[1];
if(k>weight[1]){
printf("-1\n");
return ;
}
int now=1;
while(1){
for(int i=1;i<=26;i++)
if(k>weight[mp[now][i]]){
k-=weight[mp[now][i]];
}else{
now=mp[now][i];
ans[++top]='a'+i-1;
break;
}
if(k<=siz[now])break;
else k-=siz[now];
}
ans[++top]=0;
printf("%s\n",ans+1);
}
}sam;
int main()
{
scanf("%s",S+1);
scanf("%d %d",&fla,&k);
int len=strlen(S+1);
for(int i=1;i<=len;i++)sam.ins(S[i]-'a'+1,i);
sam.solve();
return 0;
}

浙公网安备 33010602011771号