学习自 crashed 的《一类基础子串数据结构》摘抄及注解, 略过了一些 crashed 口中 "用不上" 的东西. 这里是速通笔记, 希望快速学习技巧的读者可以就看本篇, 但希望深入研究的读者还是看 crashed 的博客和其中提到的原论文叭.

\[\mathbb{Defining~\LaTeX~macros\dots}
\newcommand{\occ}[0]{\operatorname{occ}}
\newcommand{\ext}[0]{\operatorname{ext}}
\newcommand{\rep}[0]{\operatorname{rep}}
\newcommand{\str}[1]{\underline{\texttt{#1}}}
\newcommand{\per}[0]{\operatorname{per}}
\]

  / 一些记号. /

  • \(s[l:r]\), 字符串 \(s\) 的子串 \(s_ls_{l+1}\cdots s_{r}\), 下标从 \(1\) 开始.
  • \(\occ_s(t)\), \(t\) 作为子串在 \(s\) 中的出现次数. 即 \(\{(l,r)\mid s[l:r]=t\}\) 的大小.
  • 通常情况下, 以 \(s\) 代表母串.
  • \(T_0,T_1\), 分别指代 (\(s\) 的) 正串 SAM 的 parent 树 (反串后缀树) 和反串 SAM 的 parent 树 (正串后缀树).

  / 一些扩展. /

  我们熟知, 在 SAM 中, 我们依靠 \(\text{endpos}\) 集合将 \(s\) 本质不同的子串划分入若干等价类, 并用一个结点代表一个等价类, 形成了 DAWG 和 parent 树, 这是好的. 但从直觉上讲, 强行引入 "\(\text{end}\)", 引入 "后缀", 感觉有点束手束脚. 我们能否将 "后缀关系" 替换为 "子串关系", 构造出一个更为 general 的等价结构?

  这就是所谓 "基本子串结构" 干的事情. 这里我们先干脆地给出一些定义:

   \(\textbf{Definition 1.}\) (扩展串) 子串 \(t\) 的扩展串定义为 \(\ext(t):=t'\), 满足 \(t\) 是 \(t'\) 的子串, 且 \(\occ(t)=\occ(t')\).

  若 \(\arg\max\) 数量 \(>1\), 这些串的并一定是子串且满足条件, 因而这个概念是良的. 此外, 下面这些推论都容易感知到:

  \(\textbf{Theorem 1.}\) 若 \(t=s[l:r],t'=\ext(t)=[l':r'],t''=s[l'':r'']\), 使得 \(l'\le l''\le l\le r\le r''\le r'\), 则 \(\ext(t'')=t'\). (人话: 夹在 \(t\) 和 \(t'\) 中间的串的 \(\ext\) 还是 \(t'\).)

  模仿 SAM, 等价关系呼之欲出:

  \(\textbf{Definition 2.}\) (等价类) 子串 \(x,y\) 等价当且仅当 \(\ext(x)=\ext(y)\).

  我们说它是等价关系它就是 (雾), 证明很轻松. 此后, 还是如 SAM 记录每个结点的最长串作为代表, 我们记录每个等价类的最长串为代表元:

  \(\textbf{Definition 3.}\) (代表元) 等价类 \(g\) 的代表元为 \(\rep(g):=t\), 满足 \(t\in g\) 且 \(\ext(t)=t\).

  显然代表元存在且唯一. (那个, 咱既然是速通 ver, 能不能略过一些良定说明啊?)

  接下来是比较关键的部分, 我们将给出等价类的直观结构.

  \(\textbf{Theorem 2.}\) (阶梯划分) 在 \(s[l:r]\mapsto (l,r)\) 的作用下, \([1:|s|]^2\) 在 \(y=x\) 以上的点被等价类划分入若干个阶梯状集合, 其中 \(g\) 对应的阶梯出现次数为 \(\occ(\rep(g))\).

  \(\textbf{Example 1.}\) 设 \(s=\str{aababcd}\), 那么

\[\begin{aligned}
{\color{red}{g_1}} &= \{\str{aa},\str{aab},\str{aaba},\str{aabab},\str{aababc},\str{aababcd}\}\\
&\cup \{\str{aba},\str{abab},\str{ababc},\str{ababcd}\}\\
&\cup \{\str{ba},\str{bab},\str{babc},\str{babcd}\}\\
&\cup \{\str{abc},\str{abcd}\}\\
&\cup \{\str{bc},\str{bcd}\}\\
&\cup \{\str{c},\str{cd}\}\\
&\cup \{\str{d}\},\\
{\color{blue}{g_2}} &= \{\str{b},\str{ab}\},\\
{\color{green}{g_3}} &= \{\str{a}\}.
\end{aligned}
\]

其对应阶梯划分为 (感谢 crashed 倾情作画):


  / 一些联系. /

  好吧, 再说下去 SAM 就要被气走啦, 我们接下来看看这个结构与 \(T_0,T_1\) 的关系, 毕竟对这个结构的构建也很难离开它们.

  \(\textbf{Theorem 3.}\) 对于等价类 \(g\) 的某个完整阶梯, 其完整的一行对应的子串集合与 \(T_0\) 某个结点对应的子串集合相同, 其完整的一列对应的子串集合与 \(T_1\) 某个结点对应的子串集合相同, 并且二者在全局形成一一对应.

  (证明不太平凡, 但容易感性, 故略.)

  \(\textbf{Definition 4.}\) (周长) 等价类 \(g\) 的周长 \(\per(g)\) 定义为其一个完整阶梯的行数列数之和.

  利用 Theorem 3, 我们可以得到:

  \(\textbf{Theorem 4.}\) \(\sum_g\per(g)=\mathcal O(n)\).

  这一点便可以窥见如同 SAM 的强大.

  最后, 我们只需要将 \(T_0,T_1\) 的连边对应到等价类的行列上, 我们就完成基本子串结构的基本结构啦. 这个并不复杂: 对于 \(T_0\) 的从父亲到儿子的树边, 其从一行的左边界连向另一行的右边界; 对于 \(T_1\) 的从父亲到儿子的树边, 其从一行的上边界连向另一行的下边界. 如图, 对于 \(S=\str{aababcd}\):

其基本子串结构连边为


  / 一个算法. /

  乐, 我研究的论文就没有这个部分. (

  建正反 SAM 需要我教吗? 呐呐, 需要雨兔教教吗?

  识别代表元 这里就沿用一点代码里的常用记号了. 显然, 设子串 \(t\) 在正反串中分别对应 \(u,v\), 则 \(t\) 是子串等价于 \(\max_u=\max_v=|t|\), 我们可以在正 SAM 上沿着 \(\max_v=\max_u+1\) 的 DAWG 边遍历, 在后端加字符即在反 SAM 上用 \(T_1\) 的边转移, 这样就能建立结点对应顺别求出代表元了. 复杂度是 \(\mathcal O(n|\Sigma|)\) 的.

  咱还是放个代码叭.

std::function<void(int, int)>
match = [&](const int u, const int v)->void {
bool flg = sam[0].mx[u] == sam[1].mx[v];
if (flg) sam[0].bel[u] = sam[1].bel[v] = ++cnt;
rep (i, 0, 3) if (sam[0].mx[sam[0].ch[u][i]] == sam[0].mx[u] + 1) {
match(sam[0].ch[u][i], flg ? sam[1].son[v][i] : v);
}
};
match(1, 1);

  其中 son[u][i] 指 \(u\) 点沿着 parent 树走向某个儿子, 在字符串后侧加上字符 \(i\), 到达的结点.

  划分等价类 注意到正 SAM 中, 不在等价类边界上的点一定只有一条 DAWG 出边, 连向上方行对应的 SAM 结点. 因此按照 \(\max_u\) 降序为非代表元结点标记等价类编号即可. (crashed 称可以按照结点编号倒序扫描, 原因位置.)

  行列排序 划分完等价类后, 分别把行列按照扫描顺序加入等价类, 我们就得到了等价类中行列对应的 SAM 结点序列了.


  / 一个例题. /

  嗯, 只有一个例题.

  首先, 修改只有单点修 \(\textit{wl}\), 我们直接预处理出答案关于 \(\textit{wl}\) 的线性组合系数就行了.

  另一方面, 观察 \(\textit{vl}\) 和 \(\textit{vr}\), 它们不正是描述了一个等价类的行列系数吗? 一个字符串 \(t\) 的答案的贡献总和就是 \(\occ(t)\) 倍的其所在等价类行列权值乘积. 先求出 \(\textit{vr}\), 会和 \(\textit{vr}\) 乘起来的 \(\textit{vl}\) 一定是列的一段前缀, 我们借此可以求出 \(\textit{vl}\) 的线性组合系数, 再在 \(T_1\) 的 parent 树上反向求出 \(\textit{wl}\) 的线性组合系数即可. 复杂度 \(\mathcal O(n|\Sigma|+q)\).

  的确挺板的, 如果有需要可以康康兔的代码. SuffixAutomaton 里除了 sum[] 是本题所求的, 其他东西都是板子需要的.

  哪天心情好再写道题?

Note -「基本子串结构」速通笔记的更多相关文章

  1. Note -「圆方树」学习笔记

    目录 圆方树的定义 圆方树的构造 实现 细节 圆方树的运用 「BZOJ 3331」压力 「洛谷 P4320」道路相遇 「APIO 2018」「洛谷 P4630」铁人两项 「CF 487E」Touris ...

  2. Note -「Dijkstra 求解 MCMF」

    食用前请先了解 SPFA + Dinic/EK 求解 MCMF. Sol. 总所周知,SPFA 牺牲了.于是我们寻求一些更稳定的算法求解 MCMF. 网络流算法的时间属于玄学,暂且判定为混乱中的稳定. ...

  3. Note -「Dsu On Tree」学习笔记

    前置芝士 树连剖分及其思想,以及优化时间复杂度的原理. 讲个笑话这个东西其实和 Dsu(并查集)没什么关系. 算法本身 Dsu On Tree,一下简称 DOT,常用于解决子树间的信息合并问题. 其实 ...

  4. Note -「狄利克雷前缀和」

    学到一个诡异东西,当个 Trick 处理用吧. 现在有一个形如 \(\sum \limits _{i = 1} ^{n} \sum \limits _{d | i} f(d)\) 的柿子,不难发现可以 ...

  5. Note -「矩阵树定理」学习笔记

      大概--会很简洁吧 qwq. 矩阵树定理   对于无自环无向图 \(G=(V,E)\),令其度数矩阵 \(D\),邻接矩阵 \(A\),令该图的 \(\text{Kirchhoff}\) 矩阵 \ ...

  6. Note -「多项式」基础模板(FFT/NTT/多模 NTT)光速入门

      进阶篇戳这里. 目录 何为「多项式」 基本概念 系数表示法 & 点值表示法 傅里叶(Fourier)变换 概述 前置知识 - 复数 单位根 快速傅里叶正变换(FFT) 快速傅里叶逆变换(I ...

  7. Note -「动态 DP」学习笔记

    目录 「CF 750E」New Year and Old Subsequence 「洛谷 P4719」「模板」"动态 DP" & 动态树分治 「洛谷 P6021」洪水 「S ...

  8. Note -「Lagrange 插值」学习笔记

    目录 问题引入 思考 Lagrange 插值法 插值过程 代码实现 实际应用 「洛谷 P4781」「模板」拉格朗日插值 「洛谷 P4463」calc 题意简述 数据规模 Solution Step 1 ...

  9. Note -「Mobius 反演」光速入门

    目录 Preface 数论函数 积性函数 Dirichlet 卷积 Dirichlet 卷积中的特殊函数 Mobius 函数 & Mobius 反演 Mobius 函数 Mobius 反演 基 ...

  10. [转帖]「知乎知识库」— 5G

    「知乎知识库」— 5G 甜草莓 https://zhuanlan.zhihu.com/p/55998832 ​ 通信 话题的优秀回答者 已关注 881 人赞同了该文章 谢 知识库 邀请~本文章是几个答 ...

随机推荐

  1. “应用程序无法正常启动(0xc000007)”处理办法

    前几天使用非静态方式编译了一个程序,在部分系统上运行提示缺少msvcp140.dll,就从VS2019安装目录找了一个同名文件放在了程序同级目录,程序也可以正常运行了.今天重新打开虚拟机,突然就报了这 ...

  2. 如何使用强化学习算法解决贪吃蛇问题(Neural Network Learns to Play Snake)

    相关: Neural Network Learns to Play Snake https://github.com/greerviau/SnakeAI/ RL算法是有很多baseline算法的,算法 ...

  3. chcapter 2 量子力学介绍

    2.4.3  约化密度矩阵 对于A,B 构成的两体系统,A的约化密度矩阵可通过对系统B partial trace: 具体的操作为:首先把 AB 密度矩阵写成所有 ' 基矩阵叠加' 的展开形式,即每一 ...

  4. 本地文件包含漏洞详解与CTF实战

    1. 本地文件包含简介 1.1 本地文件包含定义 本地文件包含是一种Web应用程序漏洞,攻击者通过操控文件路径参数,使得服务器端包含了非预期的文件,从而可能导致敏感信息泄露. 常见的攻击方式包括: 包 ...

  5. 焕然一新!TinyVue 组件库 UI 大升级,更符合现代的审美!

    你好,我是 Kagol,个人公众号:前端开源星球. 自从 TinyVue 组件库去年开源以来,一直有小伙伴反馈我们的 UI 不够美观,风格陈旧,不太满足现阶段审美. "TinyVue 给我的 ...

  6. PC大屏自适应

    通常来说PC端的页面并不像移动端页面那样对屏幕大小和分别率有那么强的依赖.一般的页面都是取屏幕中间的一块宽度(1280px), 两边留白, 高度随着内容的长度滚动.这样无论窗口怎么变化,页面都是可用的 ...

  7. json-lib(ezmorph)、gson、flexJson、fastjson、jackson对比,实现java转json,json转java

    json-lib(ezmorph).gson.flexJson.fastjson.jackson对比,实现java转json,json转java 本文中所讲的所有代码都在此:json-test 目前关 ...

  8. .NET9 EFcore支持早期MSSQL数据库 ROW_NUMBER()分页

    前言 NET程序员是很幸福的,MS在上个月发布了NET9.0RTM,带来了不少的新特性,但是呢,我们是不是还有很多同学软硬件都还没更上,比如,自己的电脑还在跑Win7,公司服务器还在跑MSSQL200 ...

  9. Python打包工具之pyinstaller

    前言: 近期使用PySimpleGUI开发了一款开发者工具X-助手工具,意打造成平常开发助手,无论是图片还是网址的处理等等都需要这一个工具即可,无需在网上找各个网站去找解决方案, 对于GUI的打包工具 ...

  10. C# 企业微信消息推送对接,实现天气预报推送

    C# 企业微信消息推送对接,实现天气预报推送 迷恋自留地 准备工作 需要获取的东西1. 企业Id,2.应用secret 和 应用ID 获取企业id 注册完成后,在我的企业=>企业信息=>最 ...