题目链接:
https://www.luogu.org/problem/P3975
题意:
求出所有字串的第k大子串
有两种,第一种对于出现在不同位置的相同子串算作一个子串
第二种,对于不同位置的子串算作是不相同子串
数据范围:
$1leq |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 "); 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 ",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; }