P5840 [COCI2015]Divljak【AC自动机+fail树+树上乱搞】

泄露秘密 提交于 2020-01-10 06:23:28

https://www.luogu.com.cn/problem/P5840
题意:中文题,给你n个串,然后m次操作,两种操作。
1操作,集合T加入一个串。2操作,询问集合T中出现上面的n个串的第i个几次。
首先听说解法挺多的,如果有幸学会了,后来补上吧。
做法:这里用AC自动机做吧,首先对n个串建立AC自动机,建立fail树,然后每一次往T集合中加入一个字符串,就让他到AC自动机中跑,跑到的点对应到fail树上点到根上的串表示都出现了,但不过一次加入中只每个点最多只能加1,因此需要去重,同时对于同一条链上点,需要利用DFS序加LCA处理一下。统计的时候就用树状数组吧。
下面这份代码可能写丑了,不开O2勉强卡过去。

#include "bits/stdc++.h"

using namespace std;
inline int read() {
    int x = 0;
    bool f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    if (f) return x;
    return 0 - x;
}
typedef long long ll;
const int maxn = 2000000 + 10;
const ll mod = 1000000000 + 7;
#define lowbit(x) x&-x
int nxt[maxn][26], fail[maxn], endd[maxn], tot = 0;
int modify(char *s) {
    int len = strlen(s);
    int now = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (nxt[now][id] == 0) nxt[now][id] = ++tot;
        now = nxt[now][id];
    }
//    endd[now]++;
    return now;
}
void build() {
    queue<int> qu;
    for (int i = 0; i < 26; i++) {
        if (nxt[0][i] != 0) qu.push(nxt[0][i]);
    }
    while (!qu.empty()) {
        int u = qu.front();
        qu.pop();
        for (int i = 0; i < 26; i++) {
            if (nxt[u][i] != 0) fail[nxt[u][i]] = nxt[fail[u]][i], qu.push(nxt[u][i]);
            else nxt[u][i] = nxt[fail[u]][i];
        }
    }
}

struct node {
    int v, nxt;
} ed[maxn];
int head[maxn], gcnt;
void add_ed(int u, int v) {
    ++gcnt;
    ed[gcnt].v = v;
    ed[gcnt].nxt = head[u];
    head[u] = gcnt;
}

int cnt, in[maxn], ou[maxn], dep[maxn], p[maxn][22], fa[maxn];
int n, m, dis[maxn], id[maxn], point[maxn];
void dfs(int u, int f, int deep) {
    fa[u] = f, dep[u] = deep;
    in[u] = ++cnt, point[cnt] = u;
    for (int i = head[u]; i; i = ed[i].nxt) {
        int v = ed[i].v;
        dfs(v, u, deep + 1);
    }
    ou[u] = cnt;
}
void init_lca() {
    memset(p, -1, sizeof(p));
    for (int i = 1; i <= tot; i++) p[i][0] = fa[i];
    for (int j = 1; (1 << j) <= tot; j++)
        for (int i = 1; i <= tot; i++)
            if (p[i][j - 1] != -1)
                p[i][j] = p[p[i][j - 1]][j - 1];
}
int LCA(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    int lg = 21;
    for (int i = lg; i >= 0; i--)
        if (dep[x] - (1 << i) >= dep[y])
            x = p[x][i];
    if (x == y) return x;
    for (int i = lg; i >= 0; i--)
        if (p[x][i] != -1 && p[x][i] != p[y][i])
            x = p[x][i], y = p[y][i];
    return fa[x];
}
int seg[maxn];
char s[maxn];
int low[maxn];
inline void add(int x, int val) {
    for (; x < tot + 10; x += lowbit(x)) low[x] += val;
}
inline int query(int x) {
    int ret = 0;
    for (; x; x -= lowbit(x)) ret += low[x];
    return ret;
}
int main() {
    n = read();
    for (int i = 1; i <= n; i++) {
        scanf("%s", s);
        id[i] = modify(s);
    }
    build();
    for (int i = 1; i <= tot; i++) {
        add_ed(fail[i], i);
    }
    dfs(0, -1, 0);
    init_lca();
    m = read();
    int x, y;
    while (m--) {
        x = read();
        if (x == 1) {
            scanf("%s", s);
            int root = 0, count = 0, len = strlen(s);
            for (int i = 0; i < len; i++) {
                root = nxt[root][s[i] - 'a'];
                seg[count++] = in[root];
            }
            sort(seg, seg + count);
            add(seg[0], 1);
            for (int i = 1; i < count; i++) {
                if (seg[i] != seg[i - 1]) {
                    add(seg[i], 1);
                    add(in[LCA(point[seg[i]], point[seg[i - 1]])], -1);
                }
            }
        } else {
            y = read();
            y = id[y];
            printf("%d\n", query(ou[y]) - query(in[y] - 1));
        }
    }
    return 0;
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!