• 树上SA


    树上SA

    就着例题讲吧, 不然不好说

    例题

    题目描述

    给定一颗以 1 为根包含 n 个节点的树,保证对于 2∼n 的每个节点,其父亲的编号均小于自己的编号。

    每个节点上有一个的字符,一个节点所代表的字符串定义为从当前节点一直到根节点的简单路径上经过的所有字符连起来形成的字符串。

    请你给这些字符串按照字典序排序。

    特别地,如果两个节点所代表的字符串完全相同,它们的大小由它们的父亲所代表的字符串的大小决定,如果仍相同,则由它们编号的大小决定。

    输入格式

    第一行包含一个正整数 n。

    第二行包含 n-1 个数 表示第 i + 1 个点的父亲

    第三行为一个包含 n(n <= 1e5) 个小写字母的字符串 s,

    输出格式

    输出一行 n 个正整数,第 i 个正整数表示代表排名第 i 的字符串的节点编号。

    输入输出样例

    输入

    5
    1 1 3 2
    abbaa
    

    输出

    1 5 4 2 3
    

    讲解

    先看看普通的sa

    struct SA {
        static const int N = 30000 + 9;
        char str[N]; //sa[i]表示排名i的后缀起始下标,rk[i]表示起始下标i后缀的排名
        int sa[N], rk[N], tp[N], tax[N], len, M;
        inline void sort() {
            memset(tax, 0, (M + 1) * sizeof(int));
            rep (i, 1, len) ++tax[rk[i]];
            rep (i, 1, M) tax[i] += tax[i - 1];
            per (i, len, 1) sa[tax[rk[tp[i]]]--] = tp[i];
        }
        void SuffixSort() { //字符串下标从1开始
            M = 200; len = 1;
            for (int& i = len; str[i]; ++i) rk[i] = str[i], tp[i] = i;
            --len; sort();
            for (int w = 1, p = 0; p < len; w <<= 1, M = p) {
                p = 0;
                rep (i, 1, w) tp[++p] = len - w + i;
                rep (i, 1, len) if (sa[i] > w) tp[++p] = sa[i] - w;
                sort(); swap(tp, rk); rk[sa[1]] = p = 1;
                rep (i, 2, len)
                    rk[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]]
                        && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
            };
        }
    } sa;
    

    树上和线性有啥不同呢?

    那就是线性顺序变成了, 一条树链上的线性顺序, 来个ST表,就很好了,

    并且这道题是从从叶子节点作为字符串的开始的, 很好写ST

    首先是这里

    rep (i, 1, w) tp[++p] = len - w + i;
    rep (i, 1, len) if (sa[i] > w) tp[++p] = sa[i] - w;
    

    第一个rep 没有 w, 变成了没有向上没有w个祖先

    但是第二个rep 就不行了, 要再来次基数排序

     && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
    

    这里也是, sa[i - 1] + w 变成树上 ans[sa[i - 1]][log(w)]

    代码

    这里给两份, 自取, 第一份好理解, (不是我写的)

    void get_sa()
    {
    	m=27;
    	for(int i=1; i<=n; i++)c[x[i]=s[i]]++;
    	for(int i=1; i<=m; i++)c[i]+=c[i-1];
    	for(int i=n; i; i--)sa[c[x[i]]--]=i;
    
    
    	for(int k=1, t = 0; k<=max_depp; k <<= 1, ++t)
    	{
    		int num=0;
    		for(int i=1; i<=m; i++)c[i]=0;
    		for(int i=1; i<=n; i++)
    			if(!anc[i][t])y[++num]=i;
    			else ++c[x[anc[i][t]]];
    		for(int i=1; i<=m; i++)c[i]+=c[i-1];
    		for(int i=n; i; i--)if(anc[i][t])y[num+c[x[anc[i][t]]]--]=i;
    
    
    		for(int i=0; i<=m; i++)c[i]=0;
    		for(int i=1; i<=n; i++)c[x[y[i]]]++;
    		for(int i=1; i<=m; i++)c[i]+=c[i-1];
    		for(int i=n; i; i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
    
    
    		swap(x,y);
    		x[sa[1]]=num=1;
    		for(int i=2; i<=n; i++)
    			x[sa[i]]=mat(sa[i],sa[i-1],t)?num:++num;
    		if(num>=n)break;
    		m=num;
    	}
    }
    

    这是我写的

    struct STFrom {
        static const int N = 5e5 + 5;
        int f[N][20], dep[N], lg[N], t;
        int* h, * ne, * to;
        void init(int n, int* H, int* Ne, int* To) {
            t = log2(n - 1) + 1;
            h = H, ne = Ne, to = To;
            rep(i, 1, n) dep[i] = 0;
            rep(i, 1, n) lg[i] = lg[i >> 1] + 1;
        }
        void bfs(int s) {
            queue<int> q; q.push(s); dep[s] = 1;
            while (!q.empty()) {
                int x = q.front(); q.pop();
                for (int i = h[x]; i; i = ne[i]) {
                    int y = to[i];
                    if (dep[y]) continue;
                    dep[y] = dep[x] + 1; f[y][0] = x;
                    rep (j, 1, t) f[y][j] = f[f[y][j - 1]][j - 1];
                    q.push(y);
                }
            }
        }
    } ST;
    
    struct SA { //树上后缀数组要用倍增, 倍增是以叶子节点到根节点, 跟一般不同
        static const int N = 5e5 + 9;
        char str[N]; //sa[i]表示排名i的后缀起始下标,rk[i]表示起始下标i后缀的排名
        int sa[N], rk[N], tp[N], tax[N], len;
        int rk2[N], rkk[N]; //树上sa新增
        inline void sort(int* sa, int* rk, int* tp, int M) { //要排两次序
            memset(tax, 0, (M + 1) * sizeof(int));
            rep(i, 1, len) ++tax[rk[i]];
            rep(i, 1, M) tax[i] += tax[i - 1];
            per(i, len, 1) sa[tax[rk[tp[i]]]--] = tp[i];
        }
        void SuffixSort() { //俩个串相同比较父亲串,再比较这俩串的节点
            int p; len = 1;
            for (int& i = len; str[i]; ++i) rk2[i] = str[i] - 'a' + 1, tp[i] = i;
            --len; sort(sa, rk2, tp, 30); rk[sa[1]] = rkk[sa[1]] = p = 1;
            rep(i, 2, len) {
                rk[sa[i]] = rk2[sa[i - 1]] == rk2[sa[i]] ? p : ++p;
                rkk[sa[i]] = i;
            }
            for (int w = 1, t = 0; w < len; w <<= 1, ++t) {
                rep(i, 1, len) rk2[i] = rkk[ST.f[i][t]];
                sort(tp, rk2, sa, len); sort(sa, rk, tp, p);
                swap(rk, tp); rk[sa[1]] = rkk[sa[1]] = p = 1;
                rep(i, 2, len) {
                    rk[sa[i]] = tp[sa[i - 1]] == tp[sa[i]]
                        && tp[ST.f[sa[i - 1]][t]] == tp[ST.f[sa[i]][t]] ? p : ++p;
                    rkk[sa[i]] = i;
                }
            }
            rep(i, 1, len) rk[i] = rkk[i];
        }
    } sa;
    
    const int N = 5e5 + 5;
    
    int n, m, _, k;
    int h[N], to[N], ne[N], tot;
    
    void add(int u, int v) { ne[++tot] = h[u]; to[h[u] = tot] = v; }
    
    int main() {
        IOS; cin >> n;
        rep(i, 2, n) cin >> m, add(m, i);
        cin >> sa.str + 1; ST.init(n, h, ne, to);
        ST.bfs(1); sa.SuffixSort();
        rep(i, 1, n) cout << sa.sa[i] << ' ';
        return 0;
    }
    
  • 相关阅读:
    力扣(LeetCode)验证回文串 个人题解(C++)
    力扣(LeetCode)平方数之和 个人题解
    Exclusive Access 2 UVA
    C语言中指针*p[N], (*P)[N]的区别
    2018年蓝桥杯国赛比赛心得
    2018acm/icpc西安邀请赛比赛心得
    [最小割]Cable TV Network UVA
    Tree Reconstruction UVA
    Twenty Questions UVA
    python中的enumerate 函数(编号的实现方式)
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/14149718.html
Copyright © 2020-2023  润新知