BZOJ 1692 [Usaco2007 Dec]隊列變換 後綴數組

題目大意:給出一個包含大寫字母的長度爲n的字符串,每次可以從字符串的首或尾取出一個字符放在新串的末尾,輸出字典序最小的新串

當首尾相同時,找第一個不同的位置誰小取誰,然而複雜度是O(n)的。

可以將正反串中間用符號隔開連在一起,求出後綴數組,這樣取rank較小的就可以了

#include <cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
#define N 60005
using namespace std;
int n,len,sa[N],rnk[N];
char s[N];
void Radix_sort(int key[]) {
    static int t[N],tmp[N];
    for(int i=0;i<=len;i++) t[i]=0;
    for(int i=1;i<=len;i++) t[key[i]]++;
    int lim=max(len,'Z');
    for(int i=1;i<=lim;i++) t[i]+=t[i-1];
    for(int i=len;i;i--) tmp[t[key[sa[i]]]--]=sa[i];
    for(int i=1;i<=len;i++) sa[i]=tmp[i];
    return ;
}
void get_SA() {
    static int x[N],y[N];
    for(int i=1;i<=len;i++) x[i]=s[i], sa[i]=i;
    Radix_sort(x);
    int tot=0;
    for(int j=1;j<=len;j++) {
        if(j==1 || x[sa[j]]!=x[sa[j-1]] || y[sa[j]]!=y[sa[j-1]]) tot++;
        rnk[sa[j]]=tot;
    }
    for(int i=1;i<=len;i*=2) {
        for(int j=1;j<=len;j++)
            x[j]=rnk[j], y[j]=i+j>len ? 0 : rnk[i+j], sa[j]=j;
        Radix_sort(y), Radix_sort(x);
        tot=0;
        for(int j=1;j<=len;j++) {
            if(j==1 || x[sa[j]]!=x[sa[j-1]] || y[sa[j]]!=y[sa[j-1]]) tot++;
            rnk[sa[j]]=tot;
        }
    }
    return ;
}
int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%s",s+i);
    len=n*2+1;
    s[n+1]='@';
    for(int i=0;i<n;i++) s[len-i]=s[i+1];
    get_SA();
    int l=1,r=n+2;
    for(int i=1;i<=n;i++) {
        if(rnk[l]<rnk[r] && l<=n) printf("%c",s[l++]);
        else printf("%c",s[r++]);
        if(i%80==0) printf("\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章