洛谷P2336 [SCOI2012]喵星球上的点名

用广义SAM写的。特意加了注释。感觉对SAM的感觉又深入了一些。

#include <cstdio>
#include <map>
#define N 100010
using namespace std;
inline char gc() {
    static char now[1<<16], *S, *T;
    if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;}
    return *S++;
}
inline int read() {
    int x = 0, f = 1; char c = gc();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc();}
    while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = gc();}
    return x * f;
}
int str[N], len1[50010], len2[50010];
int n, m, tot;
struct SAM {
    int sz, last, mx[N<<1], fa[N<<1], ans[50010];
    map<int, int> nxt[N<<1];
    int las[N<<1], siz[N<<1]; //这个节点目前属于哪个模式串(随着i的增大,也应该越来越大),以及这个节点集合共包含于多少个模式串
    int marked[N<<1]; //作为终点出现过几次
    inline void extend(int c) {
        int p = last, np = ++sz;
        last = np;
        mx[np] = mx[p] + 1;
        for(; p && !nxt[p].count(c); p = fa[p]) nxt[p][c] = np;
        if(!p) fa[np] = 1;
        else {
            int q = nxt[p][c];
            if(mx[p] + 1 == mx[q]) fa[np] = q;
            else {
                int nq = ++sz;
                mx[nq] = mx[p] + 1;
                nxt[nq] = nxt[q];
                fa[nq] = fa[q]; fa[q] = fa[np] = nq;
                for(; nxt[p][c] == q; p = fa[p]) nxt[p][c] = nq;
            }
        }
    }
    inline void update1(int x, int y) {for(; x && las[x] != y; x = fa[x]) {++siz[x]; las[x] = y;}} //用las数组是因为,相同的las只算一次(就比如两个属于同模式串的节点,都能跳到根,但只应该算一次) 
    inline void update2(int x, int y) {for(; x && las[x] != y; x = fa[x]) {las[x] = y; ans[y]+= marked[x];}} //正常统计,las数组用途同上(这是一个很好的技巧) 
    inline void build() {
        sz = 1; tot = 0;
        for(int i = 1; i <= n; ++i) {
            len1[i] = read(); last = 1; //广义SAM
            for(int j = 1; j <= len1[i]; ++j) {
                str[++tot] = read();
                extend(str[tot]);
            }
            len2[i] = read(); last = 1;
            for(int j = 1; j <= len2[i]; ++j) {
                str[++tot] = read();
                extend(str[tot]);
            }
        }
        tot = 0;
        for(int i = 1; i <= n; ++i) { //整理每个节点的siz
            for(int j = 1, x = 1; j <= len1[i]; ++j) update1(x = nxt[x][str[++tot]], i);
            for(int j = 1, x = 1; j <= len2[i]; ++j) update1(x = nxt[x][str[++tot]], i);
        }
    }
    inline void solve() {
        while(m--) {
            int len = read();
            bool flag = 0; int x = 1;
            for(int i = 1; i <= len; ++i) {
                int d = read();
                if(!flag) {
                    if(nxt[x][d]) x = nxt[x][d];
                    else flag = 1; //匹配失败了是不能跳fa的,因为要求完美匹配。但依旧不可以break,因为尚未读完。
                }
            }
            if(!flag) ++marked[x], printf("%d\n", siz[x]);
            else puts("0");
        }
        int tot = 0;
        for(int i = 1; i <= sz; ++i) las[i] = 0;
        for(int i = 1; i <= n; ++i) {
            for(int j = 1, x = 1; j <= len1[i]; ++j) update2(x = nxt[x][str[++tot]], i);
            for(int j = 1, x = 1; j <= len2[i]; ++j) update2(x = nxt[x][str[++tot]], i);
        }
        for(int i = 1; i <= n; ++i) printf("%d ", ans[i]);
    }
}Sam;
int main() {
    n = read(); m = read();
    Sam.build(); Sam.solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值