2017 icpc青島站 J. Suffix 字尾陣列
阿新 • • 發佈:2018-11-22
題意:給你n個字串,你必須按照輸入順序在每個字串裡取一個字尾子串,然後拼接起來,使得其字典序最小,並輸出拼接後的字串。
思路:咋一看好像對每一個字串求一次sa陣列,找到排行第一的那個字尾串,然後拼接起來就ok,但是實際一想,顯然不是,比如aabaa cc,如果按照上面的演算法,得到aac,但是答案卻是aabaac,因為後面的答案會影響前面的字尾串大小,因此我們得先求出最後的最小字尾串,然後將其加到上一個串的結尾,再求前面的串的sa陣列再去找最小的字尾串,以此類推再上一個串即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<iostream> #include<vector> using namespace std; const int maxn=5e5+10; struct S_array { char s[maxn]; int sa[maxn],t[maxn],t2[maxn],c[maxn],n; void build_sa(int m) { int i,*x=t,*y=t2; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[i]=s[i]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i; for(int k=1;k<=n;k<<=1){ int p=0; for(i=n-k;i<n;i++)y[p++]=i; for(i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[y[i]]]++; for(i=0;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1;x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; if(p>=n)break; m=p; } } }sa; char ans[maxn],tmp[maxn]; string s; vector<string>V; int main() { int T; scanf("%d",&T); while(T--) { int n,cnt=0,tot; scanf("%d",&n);V.clear(); for(int i=1;i<=n;i++){ cin>>s; V.push_back(s); } for(int i=n-1;i>=0;i--) { s=V[i]; int len=s.length(),t=0; if(cnt<100+len)t=cnt;//將後面求出來的最優串的一部分拼接到這個字串的結尾 else t=len+100; //不要全部拼接,否則會超時,拼接部分即可 for(int j=0;j<len+t;j++) if(j<len)sa.s[j]=s[j]; else sa.s[j]=tmp[j-len]; sa.s[len+t]='a'-1; sa.n=len+t+1; sa.build_sa('z'+1); int j; for(j=1;;j++)if(sa.sa[j]<len)break; j=sa.sa[j]; tot=cnt; cnt=0; for(j;j<len;j++)ans[cnt++]=s[j]; for(j=0;j<tot;j++)ans[cnt++]=tmp[j]; for(j=0;j<cnt;j++)tmp[j]=ans[j]; } for(int i=0;i<cnt;i++)printf("%c",ans[i]); puts(""); } }