BZOJ3172——[Tjoi2013]单词

时间:2024-03-29 08:36:08

1、 题目大意:一篇论文是由许多单词组成,现在想知道每个单词分别在论文中出现多少次。

2、分析:对着 广义后缀自动机的图看,我们就会发现玄机,答案不就是这个单词下的后缀个数吗?

于是建立自动机,然后求出right,统计答案就好,另外说一句,right集合用基数排序之后更新一下就好

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
struct SAM{
    struct node{
        int tranc[27], fa, len, right;
    } a[2002010];
    int c[2002010], od[2002010];
    int cnt, tail;
    SAM(){
        tail = ++ cnt;
    }
    void insert(int k){
        int p, np = ++ cnt, q, nq;
        a[np].len = a[tail].len + 1;
        a[np].right = 1;
        for(p = tail; p && !a[p].tranc[k]; p = a[p].fa) a[p].tranc[k] = np;
        if(!p) a[np].fa = 1;
        else{
            q = a[p].tranc[k];
            if(a[q].len == a[p].len + 1) a[np].fa = q;
            else{
                a[nq = ++ cnt] = a[q];
                a[q].fa = a[np].fa = nq;
                a[nq].len = a[p].len + 1;
                a[nq].right = 0;
                for(; a[p].tranc[k] == q; p = a[p].fa) a[p].tranc[k] = nq;
            }
        }
        tail = np;
    }
    void init(int m){
        for(int i = 1; i <= cnt; i ++) c[a[i].len] ++;
        for(int i = 1; i <= m; i ++) c[i] += c[i - 1];
        for(int i = cnt; i >= 1; i --) od[c[a[i].len] --] = i;
        for(int i = cnt; i >= 1; i --){
            int x = od[i];
            a[a[x].fa].right+=a[x].right;
        }
        return;
    }
} sam;
char str[2000100];
int tot;
int main(){
    int n;
    scanf("%d", &n);
    tot = -1;
    for(int i = 1; i <= n; i ++){
        char ch = getchar();
        while(ch < 'a' || ch > 'z') ch = getchar();
        while('a' <= ch && ch <= 'z'){
            str[++ tot] = ch;
            ch = getchar();
        }
        str[++ tot] = 'z'+1;
    }
    tot ++;
    for(int i = 0; i < tot; i ++) sam.insert(str[i] - 'a');
    sam.init(tot);
    int num;
    int o = 0;
    for(int i = 1; i <= n; i ++){
        int now = 1, j;
        for(j = o; ((int)str[j] != 'z'+1) && (j < tot); j ++){
            now = sam.a[now].tranc[str[j]-'a'];
        }
        o = j + 1;
        printf("%d\n", sam.a[now].right);
    }
    return 0;
}