后缀数组

#define SUFFIX_ARRAY_PFXDBL
#ifdef SUFFIX_ARRAY_PFXDBL
namespace SA{
    /*
     Suffix Array, Prefix Doubling Method
     
     <SUBSCRIPT_START_FROM,SIZE>
     MAXN   : Maximum length for string
     s      : String for building suffix array <0,MAXN>
     t,t2,c : [Auxiliary] buffers <0,MAXN>
     n      : Length of string
     
     sa     : Suffix Array, Lexicographically ith suffix starts from.  <0,MAXN>
     rank   : Suffix Rank, rank of the suffix starts from i <0,MAXN>
     height : LCP of suffix starts from sa[i] and sa[i-1] <1,MAXN>
     
     build_sa(int m) : m is the size of character set("character" being 0~m-1), should set s and n previously.
     getHeight() : Calculate height and rank array accordingly.
     
     #define REP(i,t) for(int i = 0;i < t; i++)
     #define REP_R(i,t) for(int i = t-1;i >= 0; i--)
     #define REP_1(i,t) for(int i = 1;i <= t; i++)
     #define REP_1R(i,t) for(int i = t;i >= 1; i--)
     
     Time   : O(nlogn)
     Space  : 7n
     */
    const int MAXN = 10005;
    char s[MAXN];
    int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],rank[MAXN],height[MAXN],n;
    void build_sa(int m){
        int *x = t,*y = t2;
        //第一次基数排序
        REP(i, m) c[i] = 0; //重置排序数组
        REP(i, n) c[x[i]=s[i]]++; //拷贝字符串内容到x缓冲区,并在基数排序数组中统计字符数量
        REP_1(i, m-1) c[i] += c[i-1]; //基数排序数组求前缀和以算出排名
        REP_R(i, n-1) sa[--c[x[i]]] = i; //从后往前顺次查询后缀的排名写入SA(字符相同的时候,长度小的靠前,长度大的靠后)
        //此时的x数组可以看成是保存了各个位置开始的后缀对应的第一基准
        for(int k = 1;k <= n; k <<= 1){
            //第k次基数排序,单组长度2^k
            int p = 0;
            //利用SA排序第二基准,y存储的是第二基准排序之后的后缀开始位置
            for(int i = n-k;i < n; i++) y[p++] = i; //长度没达到k,这种只能看第一基准,第二基准视为0
            REP(i, n) if(sa[i] >= k) y[p++] = sa[i]-k; //从前往后扫SA,即从字典序小的后缀开始扫,如果这个后缀的开始位置>=k的话,
                                                        //这个后缀在sa中的结果实际上是i-k的第二基准
            //排序第一基准
            REP(i, m) c[i] = 0; //重置排序数组
            REP(i, n) c[x[y[i]]]++; //按第二基准顺序读取第一基准进行统计
            REP(i, m) c[i] += c[i-1];//基数排序数组求前缀和以算出排名
            REP_R(i, n-1) sa[--c[x[y[i]]]] = y[i];//按第二基准从后往前顺次查询后缀的排名写入SA
            //准备下一次排序的第一基准序列x
            //第二基准排序结果y没有用了,上一次的第一基准序列x还有用,直接互换
            swap(x, y);
            //现在y是上一次的第一基准序列
            p = 1;
            x[sa[0]] = 0;//第一基准从0开始
            //现在的sa是排好序的,从小到大查询,如果两项无法分出先后,就令成统一基准,否则基准自增
            //如果两个后缀的第一关键字和第二关键字一样,说明他们的排名一样
            REP_1(i, n-1) 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; //如果基准数已达n,说明排序完成,所有后缀已经分出先后
            m = p; //下一轮排序的基准数是p
        }
    }
    void getHeight(){
        int i,j,k = 0;
        REP(i, n) rank[sa[i]] = i;
        for(i = 0;i < n; height[rank[i++]] = k)
            for(k?k--:0,j = sa[rank[i]-1];s[i+k]==s[j+k];k++);
        return;
    }
}
#endif

  

原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/5965281.html