一些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. wireshark 报文颜色

    在使用wireshark抓包分析的过程中,默认会对不同的包进行着色,截图如下: 对不同的颜色有了解,可快速的过滤包或分析请求. 菜单栏选择视图-->着色规则,即可看到不同颜色代表的含义: 大致可 ...

  2. 使用 golang 开发 PHP 扩展

    使用 golang 开发 PHP 扩展 环境 golang go1.19.9 darwin/arm64 Macos/Linux PHP8.1.11 编译安装 实战 PHP脚手架生成 进入PHP源码,使 ...

  3. [转帖]一个小操作,SQL 查询速度翻了 1000 倍

    https://tidb.net/book/tidb-monthly/2022/2022-04/usercase/sql-1000 背景介绍​ 某一天早上来到公司,接到业务同学反馈,线上某个SQL之前 ...

  4. JVM 堆外内存查看方法

    JVM 堆外内存查看方法 JVM 堆外内存查看方法 1.概述 是否曾经想过为什么Java应用程序通过众所周知的*-Xms和-Xmx调整标志消耗的内存比指定的数量大得多 ?由于各种原因和可能的优化,JV ...

  5. [转帖]kubelet 原理解析六: 垃圾回收

    https://segmentfault.com/a/1190000022163856 概述 在k8s中节点会通过docker pull机制获取外部的镜像,那么什么时候清除镜像呢?k8s运行的容器又是 ...

  6. [转帖]CPU Utilization is Wrong

    Brendan Gregg's Blog home CPU Utilization is Wrong 09 May 2017 The metric we all use for CPU utiliza ...

  7. 计划任务方式定期获取jvm dump的方法

    说明 产品最近有一些问题,想着能够每隔一段时间抓取一下dump文件. 需求 可以定期抓取, 需要注意磁盘空间的使用. 实现方法 定时任务使用 crontab 计划任务来做 预定义获取jvm dump的 ...

  8. 解决Word等打开嵌入的文件提示 包含有害内容 无法打开的问题

    最近打开文件时提示: 从网上找了一下 最简单的解决办法是: 新建一个文件, 输入如下内容 导入注册表 每次打开时不进行 文件有效性的检查即可. 为了省事 我多加了几个版本的 如果是excel  将 w ...

  9. 信创-飞腾CPU路线图

  10. echarts去掉y轴线、设置x轴线的颜色、x轴文字颜色

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...