一些SAM的 基础 题目。(主要是我不想写SAM的原理啊啊啊)

有的题目是SA的思维题,但是可以用SAM平推,基本上可以不动脑子。

除非有特殊说明,否则将字符集看作所有小写字母,构造SAM复杂度记为 \(O(|S|)\)

基础SAM

Luogu【模板】后缀自动机 (SAM)

求 \(S\) 串中出现次数不为 \(1\) 的子串的出现次数乘上子串长度的最大值。

对于某一个等价类对应的节点 \(v\) ,如果 \(|endpos(v)|\neq 1\),它可能会对答案做出 \(len(v)\times |endpos(v)|\) 的贡献。而 \(|endpos(v)|\) 等于 \(link\) 树上子树内为 \(S\) 串节点的个数。

总体复杂度 \(O(|S|)\) 。

Luogu P2408 不同子串个数

求 \(S\) 串的不同子串个数。

等价于 SAM 上路径数,DP求值即可。

总体复杂度 \(O(|S|)\) 。

SPOJ LCS-Longest Common Substring

求串 \(S\) 和 \(T\) 的最长公共字串。

等价于求 \(T\) 前缀中在 \(S\) 的 SAM 上出现的最长后缀的最大值。

考虑在 \(S\) 的 SAM 上跑 \(T\) 串,假设当前在点 \(v\) ,最长后缀长度为 \(l\) ,加入字符 \(t_i\) 。

如果 \(v\) 存在 \(t_i\) 对应转移, \(v\leftarrow nxt_v(t_i),l\leftarrow l+1\) 即可,

否则 \(v\leftarrow link(v),l\leftarrow len(link(v))\) ,及将 \(v\) 跳向 \(link\) 边,直至跳至 \(-1\) 或存在 \(t_i\) 的转移为止。

由于 \(l\) 只会变大 \(|T|\) 次,变小 \(O(|T|)\) 次,所以总体复杂度为 \(O(|S|+|T|)\) 。

[SDOI2016]生成魔咒

动态求不同字串个数。

不同字串个数也等价于 \(\sum len(v)-len(link(v))\) ,每当加入一个新节点的时候,改变 \(link(v)\) 的节点数是 \(O(1)\) 的,可以直接暴力维护变化量。

总体复杂度 \(O(n\log V)\)

[TJOI2019]甲苯先生和大中锋的字符串

求 \(S\) 串中出现 \(k\) 次的子串中出现次数最多的长度。

可以 \(O(|S|)\) 时间内建出 SAM 并求出每个节点对应的 \(|endpos(v)|\) ,其中 \(|endpos(v)|=k\) 的会对 \([len(link(v))+1,len(v)]\) 做出 \(1\) 的贡献,线段树维护即可。

单次复杂度为 \(O(|S|log|S|)\) 。

CF802I Fake News (hard)

求出每个子串在 \(S\) 中出现次数的平方和。

对于每个 \(v\) 对应的出现次数和子串个数可知,暴力求和即可。

单次复杂度为 \(O(|S|)\) 。

[TJOI2015]弦论

求出 \(S\) 中第 \(k\) 小的子串。

每一个字串对应 \(SAM\) 上的一条路径,先 DP 一边然后确定在每一个节点的走向即可。

总体复杂度为 \(O(|S|)\) 。

[BJOI2020] 封印

给定 \(S,T,l_i,r_i\) ,求 \(S[l_i\dots r_i]\) 和 \(T\) 的最长公共子串。

先建出 \(T\) 的 SAM ,对于 \(S\) 的每一个前缀,求出最长的是 \(T\) 子串的后缀 \(a_i\) 。

最终答案相当于是 \(\max\limits_{i=l}^r(\min(a_i,i-l+1))\) 。

将所有询问离线下来,用线段树维护答案即可。

总体复杂度为 \(O(|T|+|S|\log|S|+Q\log |S|)\) 。

CF235C Cyclical Quest

给定串 \(S\) ,询问 \(T\) 的所有循环同构在 \(S\) 中的出现次数之和。

考虑将 \(T\) 变成 \(TT\) ,题目就等价于询问 \(TT\) 中所有不同的长度为 \(|T|\) 的子串在 \(S\) 中的出现次数之和。

对 \(S\) 建 SAM ,维护 \(TT\) 在 \(S\) 中的匹配,可以一直跳 \(link\) 跳到 \(len(link(v))\leqslant |T|\) 为止,由于需要不同的串,所以SAM中的每一个点只会对答案做一次贡献。

总体复杂度 \(O(|S|+\sum|T|)\) 。

CF427D Match & Catch

问 \(S\) 和 \(T\) 中均只出现过一次的串的长度的最小值。

考虑建出 \(S\) 串的 SAM ,让 \(T\) 在上面跑匹配。

与一般匹配不同的是,我们还需要维护 \(v\) 的所有 \(link\) 树上祖先,都记录下长度为多少的串出现了多少次。

但由于这个值是单调的,所以只需要维护出经过它的最大的两个 \(\min(len(v),l)\) 即可。

最后在所有 \(|endpos(v)|=1\) 的点中找答案。

总体复杂度 \(O(|S|^2)\) 。

[HAOI2016] 找相同字符

给定两个字符串 \(S_1\) 和 \(S_2\),问有多少个对不同的 \(S_1\) 的子串和 \(S_2\) 的子串相等。

考虑某一个字符串的贡献,等于 \(在S_1中的出现次数\times 在S_2中的出现次数\)。

对应到 \(S_1|S_2\) 上,找到该字符串对应节点,贡献即为 \(|endpos\cap S_1|\times|endpos\cap S_2|\)。

所以,最终答案为 \(\sum (len(v)-len(link(v)))|endpos(v)\cap S_1|\times|endpos(v)\cap S_2|\)。

总体复杂度 \(O(|S_1|+|S_2|)\)。

[CTSC2012]熟悉的文章

给定 \(m\) 个 \(01\) 串 \(T\) ,询问 \(n\) 个串 \(S\) ,问满足存在一种 \(S\) 拆分方式,使得长度大于 \(L\) 的是 \(m\) 中某一个串的子串的串的总长度 \(\geqslant 0.9|S|\) 。

考虑将所有的串 \(T\) 拼在一起,在间隔处插入一个标识符,记为 \(T_0\) ,这样可以对所有 \(T\) 一起建 SAM 保证复杂度。

找到 \(S\) 中每一个前缀在 \(T_0\) 的最长后缀匹配,记为 \(a_i\) ,考虑二分答案求解 \(L\) 。

使用 DP 维护单次转移:记 \(f_i\) 表示前 \(i\) 位,与 \(T_0\) 匹配长度的最大值。

则 \(f_i=\max(f_{i-1},\max\limits_{i-a_i\leqslant j\leqslant i-L}(f_j+i-j))=\max(f_{i-1},\max\limits_{i-a_i\leqslant j\leqslant i-L}(f_j-j)+i)\) 。

由于 \(i-a_i\) 与 \(i-L\) 都是不减的,可以使用单调队列维护转移。

整体复杂度 \(O(\sum|T|+\sum|S|\log |S|)\) 。

CF653F Paper task

给定一个长为 \(n\) 的由'('和')'组成的字符串,问其有多少个本质不同的子串是合法的括号串。

考虑建出SAM,一个字符串的贡献我们在它对于 \(endpos\) 集合中最小的位置计算,则所有的本质不同字符串对应 \(O(n)\) 个形如 \(S[l,r],l\in [a,b]\) 的区间。

使用动态开点线段树维护以每一个位置为结尾有那些起点对应合法的括号串,直接区间查询即可。

时间复杂度 \(O(n\log n)\)。

SAM+可持久化线段树合并

有的时候我们需要在线快速查询 SAM 上某一个节点的 \(endpos\) 集合,同时我们可以通过 \(link\) 树来得到 \(endpos\) 的信息,所以考虑使用可持久化线段树合并来实现。

给国的字符串

询问 \(Q\) 个串 \(T_i\) ,在 \(S\) 中可以找到多少个互不相交的子串 \(T_i\) 。

记 \(n=|S|,m=\sum|T_i|\) 。

如果已经知道了 \(T_i\) 作为 \(S\) 的字串出现的所有位置,不难证明贪心的从左到右选可以选的是最优的策略。

尝试维护这个贪心的过程,我们可以通过在 SAM 上维护每一个节点的 \(endpos\) 来实现 \(ans\log n\) 的查询。

但是,我们发现对于 \(ans\) 极大,如 aaaaa...aa 出现的次数,会被卡到 TLE 。

考虑根号分治: 对于 \(|T_i|>B\) ,必然存在 \(ans<\frac{n}{B}\) ,暴力求解的最劣复杂度为 \(O(\frac{n}{B}\log n)\) ;对于 \(|T_i|<B\) ,我们考虑预处理出来所有的长度 \(\leqslant B\) 的 \(S\) 的字串,做到在 SAM 中匹配到对应节点之后 \(O(1)\) 查询。

由于一次暴力维护是 \(ans\log n\) 的,而 \(S\) 中长度 \(\leqslant B\) 的串只有 \(n+(n-1)+\dots+(n+1-B)=nB-\dfrac{B(B-1)}{2}\) 个,所以暴力维护的 \(\sum ans<nB\) ,暴力维护答案复杂度小于 \(nB\log n\) 。

由于 \(\sum |T_i|=m\) ,所以长度大于 \(B\) 的串只会出现至多 \(O(\frac{m}{B})\) 个,所以最终复杂度为 \(O(nB\log n+\dfrac{m}{B}\times \frac{n}{B}\log n)=O(nB\log n+\dfrac{mn}{B^2}\log n)\) 。

当 \(B=O(\sqrt[3]{m})\) 时,复杂度取到最优 \(O(n\sqrt[3]m\log n)\) 。

[NOI2018] 你的名字

给定字符串 \(S\) , \(Q\) 次询问,求 \(T\) 从有多少个子串是 \(S[l,r]\) 中没有出现的( \(S[l,r]\) 表示 \(S\) 串中第 \(l\) 位到第 \(r\) 位)。

首先考虑 \(l=1,r=n\) ,也就是 \(S[l,r]=S\) 时如何处理:

让 \(T\) 串在 \(S\) 串的 SAM 上跑一次匹配,找到对应每一个前缀的最大后缀匹配是多少。

然后再在 \(T\) 串的 SAM 上处理出每一个节点对应 \(endpos\) 的贡献,复杂度为 \(O(|S|+|T|)\) 。

但是考虑到我们匹配的可能是 \(S\) 的某一部分,所以我们考虑如何在匹配的过程中判断当前节点的 \(endpos\) 是否存在在 \([l,r]\) 中的点,同时它的匹配长度是不是最优的(因为会出现被在 \(l\) 处砍掉一刀的情况)。

考虑用可持久化线段树合并维护 \(endpos\) ,在完成了正常匹配之后,查询 \(endpos\cap[1,r]\) 中最大的点 \(g\) ,如果 \(g\) 不存在或 \(g<l\) ,则需要跳到 \(link(v)\) ,重复此操作。

也有一种可能,就是 \(\min(p,g-l+1)<len(link(v))\) ,其中 \(p\) 为最长匹配长度,这中情况下选择 \(v\) 不优于选择 \(link(v)\) ,所以也要跳到 \(link(v)\) 。

总体复杂度 \(O(|S|+\sum|T|\log|S|)\) 。

CF1037H Security

\(Q\) 次询问,询问 \(S[l,r]\) 中字典序大于 \(T\) 的字典序最小的串。

考虑建出 \(S\) 的 SAM ,同时使用可持久化线段树合并维护 \(endpos\) 。

将 \(T\) 在 \(S\) 中进行匹配,维护每一次对应匹配的字符在 \(S\) 中的位置,初始为 \(l-1\) ,每次找到 \([las+1,r]\) 中最小的 \(endpos\) 。

如果没有,则表示 \(T\) 无法匹配、

找到 \(T\) 在 \(S[l,r]\) 的最长前缀之后,从后往前一次枚举,知道找到某一位的存在一个大于 \(T\) 的串,弹出并输出答案。

总体复杂度 \(O(|S|+26\sum|T|\log|S|)\) ,但是显然跑不满 \(26\) 这个常数。

[HEOI2016/TJOI2016]字符串

给定字符串 \(S\),\(Q\) 次询问,求最长的 \(len\) 使得 \(S[c\dots d]\) 中长为 \(len\) 的后缀是 \(S[a\dots b]\) 的子串。

首先将 \(S\) 串反转,将前缀变成后缀。

由于答案是可二分的,考虑二分答案:

我们通过 \(link\) 树上倍增找到 \(S[d-len+1\dots d]\) 在 SAM 中的位置,查找是否存在在 \([a+len-1,b]\) 的 \(endpos\) 即可。

总体复杂度 \(O(|S|\log^2|S|)\)。

CF666E Forensic Examination

给定字符串 \(S\),和一个些字符串 \(T_1,T_2\dots T-m\),\(Q\) 次询问,问在 \(T_L T_{L+1}\dots T_R\) 中,\(S[l,r]\) 出现次数最多的串中编号最小是哪一个,并输出出现次数。

考虑建出 \(S|T_1|T_2|\dots|T_m\) 的 SAM,其中 '|' 为分隔符。

使用可持久化线段树合并维护每个点的 \(endpos\) 中位置对应第 \(T_j,j=1,2\dots m\) 个串的分别有多少个。

查询 \(S[l,r]\) 的出现次数,考虑从 \(S[1,r]\) 对应的节点进行 \(link\) 树上倍增找到对应节点,在线段树上区间取值即可。

总体复杂度 \(O(|S|+\sum|T|\log(\sum|T|)+Q\log(|S|+\sum|T|))\)。

广义SAM

有的时候我们需要几个串的子串信息,所以考虑将若干个串建到同一个 SAM 中,考虑到 SAM 中的 nxt 数组就是维护了一个 Trie 树,所以考虑在建立 Trie 树的过程中建立 SAM ,由于 SAM 中会所用到之前的节点,所以考虑在 Trie 树上广搜来实现。

Luogu【模板】 广义后缀自动机(广义 SAM)

询问若干个串 \(S_1,S_2\dots S_n\) 中一共有多少个本质不同的子串。

就是基本的建立 SAM 的过程,最后求出 \(\sum len(v)-len(link(v))\) 即可。

总体复杂度 \(O(\sum|S|)\)。

CF452E Three strings

有三个串 \(A,B,C\),对于每一个 \(l\in [1,\min(|A|,|B|,|C|)]\),问有多少对 \((a,b,c)\),满足 \(A[a,a+l-1]=B[b,b+l-1]=C[c,c+l-1]\),答案对 \(10^9+7\) 取模。

建出广义SAM,维护每一个节点对于 \(A,B,C\) 的 \(endpos\) 大小,那么它会对于 \(len(link(v))+1\) 到 \(len(v)\) 作三个集合大小乘积的贡献,差分维护即可。

总体复杂度 \(O(|A|+|B|+|C|)\)。

CF204E Little Elephant and Strings

有 \(n\) 个字符串,对于每一个串,询问其有多少个子串在至少 \(k\) 个串中出现过。

建出广义SAM,使用动态开点线段树维护出 \(endpos\) 集合在每一个串中有多少个,如果这个 \(endpos\) 中有 \(\geqslant k\) 个串,则令这些数均做 \(len(v)-len(link(v))\) 的贡献。

在 \(link\) 树上跑线段树合并即可得到答案。

总体复杂度 \(O(\sum |S|\log n)\)

SAM题目合集的更多相关文章

  1. array题目合集

    414. Third Maximum Number 给一个非空的整数数组,找到这个数组中第三大的值,如果不存在,那么返回最大的值.要求时间复杂度为o(n) 例如: Example 1: Input: ...

  2. codeforces题目合集(持续更新中)

    CF280CCF280CCF280C 期望dp CF364DCF364DCF364D 随机化算法 CF438DCF438DCF438D 线段树 CF948CCF948CCF948C 堆 CF961EC ...

  3. 前端面试经典题目合集(HTML+CSS)一

    1.说说你对HTML语义化的理解? (1)什么是HTML语义化? 根据内容的结构化(内容语义化),选择合适的标签(代码语义化)便于开发者阅读和写出更优雅的代码的同时让浏览器的爬虫和机器很好地解析. ( ...

  4. 区间DP入门题目合集

      区间DP主要思想是先在小区间取得最优解,然后小区间合并时更新大区间的最优解.       基本代码: //mst(dp,0) 初始化DP数组 ;i<=n;i++) { dp[i][i]=初始 ...

  5. JS题目合集---新技术层出不穷,打好基础才是上策~

    在IT界中公司对JavaScript开发者的要求还是比较高的,但是如果JavaScript开发者的技能和经验都达到了一定的级别,那他们还是很容易跳到优秀的公司的,当然薪水就更不是问题了.但是在面试之前 ...

  6. Java面试经典题目合集

    32 1.”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法? “static”关键字表明一个成员变量或者是成员方法与类相关,可以在 ...

  7. PTA L1题目合集(更新至2019.3)

    L1-001 Hello World (5 分) 链接:https://pintia.cn/problem-sets/994805046380707840/problems/9948051471320 ...

  8. 近几年杭电OJ大型比赛题目合集【更新到2017年11月初】

    2017年: 区域赛网络赛   6194~6205    6206~6216 区域赛网络赛   6217~6229 2016年: 区域赛网络赛  5868~5877    5878~5891    5 ...

  9. atcoder题目合集(持续更新中)

    Choosing Points 数学 Integers on a Tree 构造 Leftmost Ball 计数dp+组合数学 Painting Graphs with AtCoDeer tarja ...

  10. 最长上升子序列(LIS)题目合集

    有关最长上升子序列的详细算法解释在http://www.cnblogs.com/denghaiquan/p/6679952.html 1)51nod 1134 一题裸的最长上升子序列,由于N<= ...

随机推荐

  1. Nginx的日志处理

    Nginx的日志处理 背景 之前一直被各种咨询nginx的使用问题. 大部分都是性能, 加模块, 以及一些tcp端口等的问题. 其实这些都还好, 还有一个比较麻烦的问题时日志相关的. nginx的日志 ...

  2. Oracledb_exporter 获取表大小信息的简单方法

    Oracledb_exporter 获取表大小信息的简单方法 背景 用我儿子的现状作为背景: 我爱学习, 学习让我妈快乐. 下载exporter exporter 可以在github上面下载最新版本是 ...

  3. [转帖]Nacos使用2.0.1版本启动出现9848端口错误的解决方式(亲测有效)

    目录 一.背景 二.报错如下 三.报错原因 四.解决方式 一.背景 nacos服务端和客户端都是 2.x版本. centos7使用原始安装nacos单机版,没有使用docker安装naocs集群. 二 ...

  4. [转帖]通过配置优化KingbaseES服务器性能

    目录 1. 概述 2. 数据库应用类型 3. 服务器参数 3.1. max_connections 3.2. shared_buffers 3.3. effective_cache_size 3.4. ...

  5. [转帖]金仓数据库KingbaseES V8R6索引坏块故障处理

    案例说明: 在执行表数据查询时,出现下图所示错误,索引故障导致表无法访问,后重建索引问题解决.本案例复现了此类故障解决过程. 适用版本: KingbaseES V8R3/R6 一.创建测试环境 # 表 ...

  6. [转帖]【mmap】深度分析mmap:是什么 为什么 怎么用 性能总结

    https://blog.csdn.net/bandaoyu/article/details/106750990 目录 有什么用? 1.文件映射 2.分配内存(匿名文件映射) mmap基础概念 mma ...

  7. [转帖]xsos - Summarize system info from sosreports

    https://github.com/ryran/rsar I'M LOOKING FOR RSAR SCREEN SHOTS INTRO INSTALLATION EXAMPLES IN ACTIO ...

  8. [转帖]CPU结构对Redis性能的影响

    文章系转载,便于分类和归纳,源文地址:https://wangkai.blog.csdn.net/article/details/111571446 CPU的多核架构和多CPU架构都会影响到Redis ...

  9. GS7 备份恢复之后客户端登录报错的解决方法:COM 类工厂中 CLISID 为 xxxx的组建失败, 原因是出现了一下错误 8000401a

    1. 最近需要创建一个用户的数据库应用信息, 备份恢复之后发现有时候一些环境无法使用. COM 类工厂中 CLISID 为 xxxx的组建失败, 原因是出现了一下错误 8000401a 错误图片为 2 ...

  10. 金蝶Cosmic虚拟机简单使用与总结

    背景 知己知彼 简单学习下友商发出来的测试软件 看看有否对自己现在的工作有所指导 也看看对方的部署方式有啥优缺点 当然了仅是测试, 不是生产软件可能有失真. 注意 我没有测试序列号, 登录系统耗时很久 ...