LG3809 【模板】後綴排序
阿新 • • 發佈:2019-04-09
typedef type range auto uno clu back make fine
題意
題目背景
這是一道模板題。
題目描述
讀入一個長度為 $ n $ 的由大小寫英文字母或數字組成的字符串,請把這個字符串的所有非空後綴按字典序從小到大排序,然後按順序輸出後綴的第一個字符在原串中的位置。位置編號為 $ 1 $ 到 $ n $。
輸入輸出格式
輸入格式:一行一個長度為 $ n $ 的僅包含大小寫英文字母或數字的字符串。
輸出格式:一行,共n個整數,表示答案。
輸入輸出樣例
輸入樣例#1: 復制ababa輸出樣例#1: 復制
5 3 1 4 2
說明
$n <= 10^6$
分析
推薦博客:算法學習:後綴自動機轉後綴樹轉後綴數組。
對反串建立後綴自動機,然後用parent樹建出後綴樹,最後在後綴樹上dfs求出後綴數組。
因為後綴自動機的parent樹連起來是原串的反向前綴樹,所以對反串這樣做建立起的就是後綴樹。
對反串建立後綴自動機的時候,要額外記錄pos數組,表示節點對應的原串中後綴起始位置。這樣配合len數組便知道了後綴樹上面的邊上面的字符。
時間復雜度\(O(n)\)
代碼
字符集包含大小寫英文字母或數字,這就非常煩了……用了各種奇技淫巧才卡過這道題。
#include<bits/stdc++.h> #define rg register #define il inline #define co const template<class T>il T read(){ rg T data=0,w=1;rg char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();} while(isdigit(ch)) data=data*10+ch-'0',ch=getchar(); return data*w; } template<class T>il T read(rg T&x) {return x=read<T>();} typedef long long ll; using namespace std; co int N=2e6+10; char s[N]; int n; // SAM unordered_map<int,int> ch[N]; int pos[N],val[N],len[N],fa[N],last=1,sz=1; void extend(int c,int po){ int p=last,cur=last=++sz; pos[cur]=po,val[cur]=1,len[cur]=len[p]+1; for(;p&&!ch[p].count(c);p=fa[p]) ch[p][c]=cur; if(!p) fa[cur]=1; else{ int q=ch[p][c]; if(len[q]==len[p]+1) fa[cur]=q; else{ int clone=++sz;len[clone]=len[p]+1; ch[clone]=ch[q]; fa[clone]=fa[q],pos[clone]=pos[q]; fa[q]=fa[cur]=clone; for(;ch[p].count(c)&&ch[p][c]==q;p=fa[p]) ch[p][c]=clone; } } } // ST vector<pair<int,int> > e[N]; int sa[N],rank[N],tp; void dfs(int u){ if(val[u]) sa[::rank[pos[u]]=++tp]=pos[u]; sort(e[u].begin(),e[u].end()); for(auto i:e[u]) dfs(i.second); } int main(){ scanf("%s",s+1),n=strlen(s+1); for(int i=n;i>=1;--i) extend(s[i],i); for(int i=2;i<=sz;++i) e[fa[i]].push_back(make_pair(s[pos[i]+len[fa[i]]],i)); dfs(1); for(int i=1;i<=n;++i) printf("%d ",sa[i]); return 0; }
LG3809 【模板】後綴排序