P3975 [TJOI2015]弦論(字尾自動機)
阿新 • • 發佈:2021-07-15
對於t=0的情況:
在不同位置出現的子串算同一個子串,這裡巨大的坑點是要把字尾自動機上除起點以外的所有點的sz置為1(不管它是虛擬節點還是真實節點),這一點萌新還沒理解...
然後把所有節點按照len值從小到大排序,然後做一個倒著的轉移,每個節點首先能轉移出sz個子串,然後加上它後繼節點的貢獻,可以求出每個節點往後可以轉移出多少種子串。
然後貪心的找。
對於t=1的情況,和以前一樣,求出每個節點所代表的字串集合在母串中出現了幾次(即子樹大小)。
然後把所有節點按照len值從小到大排序,倒著轉移。
#include<bits/stdc++.h> using namespace std; const int maxn=2e6+100; int len[maxn],link[maxn],nxt[maxn][26]; int sz[maxn]; int tot=1,lst=1; string s; int n,t,k; void sam_extend (char c) { int cur=++tot; len[cur]=len[lst]+1; sz[cur]=1; int p=lst; while (p&&!nxt[p][c-'a']) { nxt[p][c-'a']=cur; p=link[p]; } if (!p) { link[cur]=1; } else { int q=nxt[p][c-'a']; if (len[p]+1==len[q]) { link[cur]=q; } else { int clone=++tot; len[clone]=len[p]+1; for (int i=0;i<26;i++) { nxt[clone][i]=nxt[q][i]; } link[clone]=link[q]; while (p&&nxt[p][c-'a']==q) { nxt[p][c-'a']=clone; p=link[p]; } link[q]=link[cur]=clone; } } lst=cur; sz[cur]=1; } vector<int> g[maxn]; void dfs (int u) { for (int v:g[u]) { dfs(v); sz[u]+=sz[v]; } } int a[maxn]; int sum[maxn]; int cmp (int x,int y) { return len[x]<len[y]; } int main () { cin>>s>>t>>k; for (char i:s) sam_extend(i); for (int i=2;i<=tot;i++) g[link[i]].push_back(i); if (!t)for (int i=2;i<=tot;i++) sz[i]=1; if (t) dfs(1); for (int i=1;i<=tot;i++) a[i]=i; sort(a+1,a+tot+1,cmp); if (t) sz[1]=0; for (int i=tot;i>=1;i--) { sum[a[i]]=sz[a[i]]; for (int j=0;j<26;j++) { if (nxt[a[i]][j]) { sum[a[i]]+=sum[nxt[a[i]][j]]; //sum[i]表示節點i往後可以轉移出多少條不同的子串 } } } if (k>sum[1]) return printf("-1\n"),0; int u=1; while (k>0) { int p=0; while (k>sum[nxt[u][p]]) { k-=sum[nxt[u][p]]; p++; } u=nxt[u][p]; printf("%c",p+'a'); k-=sz[u]; } }