• 后缀数组模板(全注释)


    题目链接:http://uoj.ac/problem/35

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define enter putchar('
    ')
    #define space putchar(' ')
    using namespace std;
    typedef long long ll;
    template <class T>
    void read(T &x){
        char c;
        bool op = 0;
        while(c = getchar(), c < '0' || c > '9')
    	if(c == '-') op = 1;
        x = c - '0';
        while(c = getchar(), c >= '0' && c <= '9')
    	x = x * 10 + c - '0';
        if(op) x = -x;
    }
    template <class T>
    void write(T x){
        if(x < 0) putchar('-'), x = -x;
        if(x >= 10) write(x / 10);
        putchar('0' + x % 10);
    }
    
    const int N = 100005;
    int n;
    char s[N];
    int buf1[N], buf2[N], buc[N], sa[N], rnk[N], height[N];
    
    void suffix_sort(){
        int *x = buf1, *y = buf2, m = 127; //m是基数排序的基数的最大值
        //buf1和buf2是两个用来缓存的临时数组,用指针定义方便循环使用
        /**** 接下来先对所有单个字符基数排序 ****/
        for(int i = 0; i <= m; i++) buc[i] = 0; //先把桶清空
        for(int i = 1; i <= n; i++) buc[x[i] = s[i]]++; //此时,x[i]是一个字符串的“值”
        for(int i = 1; i <= m; i++) buc[i] += buc[i - 1];
        for(int i = n; i; i--) sa[buc[x[i]]--] = i;
        for(int k = 1; k <= n; k <<= 1){
            //倍增枚举所比较的字符串的长度(字符串的长度的一半是k,即x[i]中此时存储的信息来自i开头长为k的字符串)
    	int p = 0; //p统计当前放入新桶的字符串的个数
            //由于上一轮排序后,sa数组中存储的顺序相当于这一轮的第二关键字排序结果
            //所以接下来把上一轮的排序结果转译成这一轮字符串的按第二关键字排序结果,把排好序的后缀放在y数组中
    	for(int i = n - k + 1; i <= n; i++) y[++p] = i;
            //对于所有右半部分为空的字符串,它们的第二关键字最小,所以排在最前面
    	for(int i = 1; i <= n; i++) if(sa[i] > k) y[++p] = sa[i] - k;
            //按顺序枚举上一轮排序后的sa数组中的字符串,如果它作为第二关键字对某个字符串有贡献
            //则将被贡献字符串放入y数组
            //至此,y数组中是1~n的后缀编号,已按第二关键字大小升序排列。
    	for(int i = 0; i <= m; i++) buc[i] = 0; //再进行一次基数排序
    	for(int i = 1; i <= n; i++) buc[x[y[i]]]++;
            //还记得x[i]存的是什么吗?是i开头长为k的字符串的“值”,即第一关键字
    	for(int i = 1; i <= m; i++) buc[i] += buc[i - 1];
    	for(int i = n; i; i--) sa[buc[x[y[i]]]--] = y[i];
            /**** 接下来更新一个字符串的“值”——这次是长为2k的字符串的“值”了 ****/
    	swap(x, y), x[sa[1]] = p = 1;
            //注意这里交换了一下x和y……现在y是上一轮(长为k)的“值”,x是这一轮(长为2k)的“值”
    	for(int i = 2; i <= n; i++)
    	    if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) x[sa[i]] = p;
            //两个字符串的两个关键字完全相同,则新的“值”也相同
    	    else x[sa[i]] = ++p; //否则它对应着一个新的“值”
    	if((m = p) >= n) break; //如果每个后缀对应值都不一样,则已经排好序了
        }
        /**** 接下来记录rank数组 ****/
        for(int i = 1; i <= n; i++) rnk[sa[i]] = i;
        /**** 接下来处理height数组:sa[i]和sa[i - 1]对应字符串的LCP ****/
        for(int i = 1, j, k = 0; i <= n; i++){
    	if(rnk[i] == 1) continue;
            if(k) k--; //h[i] >= h[i - 1] - 1,k记录上一个h[i]
            j = sa[rnk[i] - 1];
            while(s[i + k] == s[j + k] && i + k <= n && j + k <= n) k++;
            height[rnk[i]] = k;
        }
    }
    int main(){
        scanf("%s", s + 1), n = strlen(s + 1);
        suffix_sort();
        for(int i = 1; i <= n; i++)
    	write(sa[i]), i == n ? enter: space;
        for(int i = 2; i <= n; i++)
            write(height[i]), i == n ? enter: space;
        return 0;
    }
    
  • 相关阅读:
    深入探究 K8S ConfigMap 和 Secret
    并非每个容器内部都能包含一个操作系统
    解析分布式系统的缓存设计
    Linux下从零开始创建lvm虚拟磁盘阵列+脚本化解决方案
    K8S Pod Pending 故障原因及解决方案
    ES 7.13版本设置索引模板和索引生命周期管理
    K8S Ingress使用|常见问题列表
    docker垃圾处理
    搭建rsyslog日志服务器
    Kubernetes中使用ClusterDNS进行服务发现
  • 原文地址:https://www.cnblogs.com/RabbitHu/p/UOJ35.html
Copyright © 2020-2023  润新知