专栏文章

DAG链剖分

算法·理论参与者 4已保存评论 3

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
3 条
当前快照
1 份
快照标识符
@miozwcjw
此快照首次捕获于
2025/12/03 03:52
3 个月前
此快照最后确认于
2025/12/03 03:52
3 个月前
查看原文
本文介绍的是针对于 SAM 上的 DAG 链剖分。
前置知识:SAM,树链剖分。
众所周知,我们可以快速在 SAM 上找到一个字符串,并且可以用一个节点在 parent 树上到根节点的路径表示一个字符串的所有后缀,但是无法很方便地表示一个字符串的所有前缀。
考虑一个字符串的所有前缀在 SAM 上对应的点是什么。
由于在一个字符串末尾添加字符相当于在 SAM 的 DAG 上走一条边,所以一个字符串的所有前缀对应了 DAG 上的一条路径。
考虑用类似树链剖分的思想,将 DAG 剖分成若干条重链,使得任意一条路径都可以拆分为 O(logn)O(\log n) 段重链。
fxf_x 表示在 DAG 上以 xx 结尾的路径条数,gxg_x 表示以 xx 开头的路径条数。
FxF_x 表示 xx 连向的点中 gg 最大的那个,GxG_x 表示连向 xx 的点中 ff 最大的那个,如果有最大值相同则任意取。
如果对于一条边 x,yx,y,满足 Fx=yF_x=yGy=xG_y=x,则将这条边设置为重边,其余边设置为轻边。
则任意一条路径都能被剖分为 O(logn)O(\log n) 条重链。
证明:
首先,gx=1+(x,y)Egyg_x=1+\sum\limits_{(x,y)\in E}g_y
xx 连向的点 uu 满足 uFxu\neq F_x,则 gugFx,gu<gxgFxg_u\le g_{F_x},g_u<g_x-g_{F_x},所以 gugx2g_u\le \frac {g_x}2
同理,若连向 xxuu 满足 uGxu\neq G_x,则 fufx2f_u\le \frac{f_x}2
pxp_x 表示 xx 这个前缀在 DAG 上对应的点,显然 fpxf_{p_x} 单调递增,gpxg_{p_x} 单调递减。
如果边 px,px+1p_x,p_{x+1} 是轻边,则要么 ff 至少乘 22,要么 gg 至少除以 22,又因为 SAM 的 DAG 上总路径条数是关于 nn 的多项式级别的,所以一条路径上不会有超过 O(logn)O(\log n) 条轻边。
具体实现可以直接通过二分哈希,实现 O(log2n)O(\log^2n) 单次剖分。使用后缀数组求 lcp 可以做到 O(logn)O(\log n) 剖分。
有了这个做法后,我们可以做很多东西。
求区间最短 border、最长 border、border 个数。
考虑一个前缀是 border 的条件,也就是若 S[l,i]S_{[l,i]}S[l,r]S_{[l,r]} 的后缀,则 S[l,i]S_{[l,i]}S[l,r]S_{[l,r]} 的 border。
AABB 后缀相当于 AA 在 SAM 的 parent 树上是 BB 的祖先,所有 border 相当于 S[l,r]S_{[l,r]} 的所有在 SAM 上对应的点是 xx 的祖先的前缀。
DAG 链剖分把 S[l,r]S_{[l,r]} 所有前缀剖分成 O(logn)O(\log n) 个区间,查询变成了一个点祖先中所有编号在一个区间内的点,直接主席树即可。
把模式串在 SAM 上标出来,问题转化为求一个区间所有子串的权值和。
预处理出来每个点的祖先的权值和,DAG 链剖分后转化为区间和,直接求前缀和即可。
但是这样可能会多算在同一个节点上的情况,多算的贡献是一个二维数点,总复杂度 O(nlogn+qlog2n)O(n\log n+q\log^2 n)

评论

3 条评论,欢迎与作者交流。

正在加载评论...