题解 CF666E Forensic Examination

link

题意

给定一个长度为 n 的字符串 sm 个字符串 t1mq 次询问,每次查询 s[pl:pr]tlr 中最多的出现次数。

数据范围:1n,q,|t|5×1051m5×104

题解

m 个串建一个广义 SAM,对 SAM 中的每个节点建一棵下标为所属串的动态开点权值线段树,记录区间最大值与其对应下标 。

然后建立 parent tree,在上面跑一个线段树合并,合并的方式只能是新建节点,直接在原树上合并会改变其他子节点的结构。

类似求 LCS,处理出 i[1,n],s[1:i] 在 SAM 匹配的最长后缀的长度 maxli 及对应节点 posi

每次查询,先判没有匹配的情况,即 rl+1>maxlpr,然后从 pospr 开始在 parent tree 上 倍增,找到 minlenu 满足 lenuplpr+1,此时点 u 恰好包含了 s[pl:pr] 这一状态,直接查询点 u 的权值线段树中 [l,r] 中的最大值即可。

认为 n,q,|t| 同级,则时间复杂度为 O(nlogn)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, Log = 20, S = 26;
struct node {
    int mx, cnt;
    node(int Mx = 0, int Cnt = 0) { mx = Mx, cnt = Cnt; }
    friend bool operator < (const node &qwq, const node &awa) {
        return qwq.cnt == awa.cnt ? qwq.mx > awa.mx : qwq.cnt < awa.cnt;
    }
    friend node operator + (const node &qwq, const node &awa) {
        return node(qwq.mx, qwq.cnt + awa.cnt);
    }
};
struct edge {
    int to, nxt;
} e[N << 1];
int head[N], ecnt;
void add(int u, int v) {
    e[++ecnt] = (edge){v, head[u]};
    head[u] = ecnt;
}
int n, m, q;
int fa[Log][N], dep[N], pos[N], maxl[N];
char s[N], t[N];
namespace SegmentTree {
    int rt[N], ls[N * Log], rs[N * Log], tot;
    node v[N * Log];
    void pushup(int x) { v[x] = max(v[ls[x]], v[rs[x]]); }
    void insert(int &x, int l, int r, int p) {
        if(!x) x = ++tot;
        if(l == r) {
            v[x].cnt++;
            v[x].mx = l;
            return;
        }
        int mid = l + r >> 1;
        if(p <= mid) insert(ls[x], l, mid, p);
        else insert(rs[x], mid + 1, r, p);
        pushup(x);
    }
    int merge(int a, int b, int l, int r) {
        if(!a || !b) return a + b;
        int x = ++tot;
        if(l == r) {
            v[x] = v[a] + v[b];
            return x;
        }
        int mid = l + r >> 1;
        ls[x] = merge(ls[a], ls[b], l, mid);
        rs[x] = merge(rs[a], rs[b], mid + 1, r);
        pushup(x);
        return x;
    }
    node query(int x, int l, int r, int L, int R) {
        if(!x) return node();
        if(L <= l && r <= R) return v[x];
        node res;
        int mid = l + r >> 1;
        if(mid >= L) res = max(res, query(ls[x], l, mid, L, R));
        if(mid < R) res = max(res, query(rs[x], mid + 1, r, L, R));
        return res;
    }
} using namespace SegmentTree;
namespace SAM {
    struct state {
        int len, link, ch[S];
    } st[N];
    int last, cnt = 1;
    void insert(int c, int i) {
        int p = last, q, x = ++cnt, y;
        st[x].len = st[p].len + 1;
        SegmentTree::insert(rt[x], 1, m, i);
        for(; p && !st[p].ch[c]; p = st[p].link) st[p].ch[c] = x;
        if(!p) st[x].link = 1;
        else {
            q = st[p].ch[c];
            if(st[q].len == st[p].len + 1) st[x].link = q;
            else {
                st[y = ++cnt] = st[q];
                st[y].len = st[p].len + 1;
                st[q].link = st[x].link = y;
                for(; p && st[p].ch[c] == q; p = st[p].link) st[p].ch[c] = y;
            }
        }
        last = x;
    }
    void insert(char *t, int k) {
        int len = strlen(t + 1);
        for(int i = 1; i <= len; i++)
            insert(t[i] - 'a', k); 
    }
} using namespace SAM;
void dfs(int u) {
    for(int i = head[u], v; i; i = e[i].nxt) {
        v = e[i].to;
        fa[0][v] = u;
        dfs(v);
        rt[u] = merge(rt[u], rt[v], 1, m);
    }
}
int main() {
    scanf("%s%d", s + 1, &m);
    n = strlen(s + 1);
    for(int i = 1; i <= m; i++) {
        scanf("%s", t + 1);
        last = 1, insert(t, i);
    }
    for(int i = 2; i <= cnt; i++)
        add(st[i].link, i);
    dfs(1);
    for(int i = 1; i < Log; i++)
        for(int j = 1; j <= cnt; j++)
            fa[i][j] = fa[i - 1][fa[i - 1][j]];
    for(int i = 1, c, p = 1, l = 0; i <= n; i++) {
        c = s[i] - 'a';
        if(st[p].ch[c]) p = st[p].ch[c], l++;
        else {
            for(; p && !st[p].ch[c]; p = st[p].link);
            if(!p) p = 1, l = 0;
            else l = st[p].len + 1, p = st[p].ch[c];
        }
        pos[i] = p, maxl[i] = l;
    }
    scanf("%d", &q);
    while(q--) {
        int l, r, pl, pr, len;
        scanf("%d%d%d%d", &l, &r, &pl, &pr), len = pr - pl + 1;
        if(len > maxl[pr]) { printf("%d 0\n", l); continue; }
        int u = pos[pr];
        for(int i = Log - 1; ~i; i--)
            if(st[fa[i][u]].len >= len)
                u = fa[i][u];
        node ans = query(rt[u], 1, m, l, r);
        if(!ans.cnt) ans.mx = l;
        printf("%d %d\n", ans.mx, ans.cnt);
    }
    return 0;
}
posted @   Terac  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示