SA / SAM 题目集
上一次做 SA / SAM 相关的题还要数到某场毒瘤 NOIP 模拟赛……这么久没做了都快忘光了……写点东西记录一些最近做到的水好题。
LOJ2059 「TJOI / HEOI2016」字符串
题意
给定一个长度为 \(n\) 的字符串 \(s\) ,接下来有 \(m\) 次询问。每次询问给出四个参数 \(a,b,c,d\) 。求 \(s[a,b]\) 的所有子串和 \(s[c,d]\) 的 LCP 的最大值。
\(n,m \le 10^5\) 。
题解
题目可以转化为,求一段连续的后缀与 \(s[c,d]\) 的 LCP 的最大值。由于这个最大值对位置还有一个限制,因此直接求不大好做,考虑二分答案转化为判定问题。
二分答案之后,就只需要查询,和 \(s[c,d]\) 的 LCP \(\ge k\) 的后缀是否在一段区间内出现过。这样只要把 SA 建出来,就只需要查询一个区间内是否出现了某个区间内的数,直接按照 \(rk\) 建一棵主席树,就可以在 \(O(n \log^2 n)\) 的时间内解决了。
SA + 主席树也是一个相当常见的套路,可以注意一下。另外 SAM 做这个题复杂度似乎没有优化?所以也没啥意义了。
UVA10829 L-Gap Substrings
题意
给定一个串 \(S\) 。求有多少 \(S\) 的子串是形如 \(UVU\) 的形式,且 \(U\) 不是空串,\(V\) 中恰好包含了 \(g\) 个字符。
数据组数 \(T \le 10,|S| \le 5 \times 10^4,g \le 10\) 。
题解
考虑枚举 \(U\) 串的长度。这样我只要每隔 \(|U|\) 个放一个关键点。然后对于两两相邻的关键点,将后面的关键点强行往后移 \(|V|\) 的长度,检查以这两个关键点为结尾的串的 \(\mathrm{LCS}\) 以及以这两个关键点开头的串的 \(\mathrm{LCP}\),就可以快速统计对于某个串长的答案。这样利用 \(\mathrm{SA}\) 就可以快速实现这个东西了,复杂度是 \(O(n \log n)\) 的。
和刚刚那个题一样,先枚举产生贡献的串长,然后每隔固定长度放一个关键点。每次检查关键点的信息也是一个常见的套路。类似的题还有「NOI2016」优秀的拆分。
Codeforces 700E Cool Slogans
题意
给出一个长度为 \(n\) 的字符串 \(s_1\)。定义一个字符串序列 \(s_{1 \sim k}\) ,满足性质:\(s_i\) 在 \(s_{i - 1} (i \ge 2)\) 中出现至少两次,问最大的 \(k\) 是多少,使得从 \(s_1\) 开始到 \(s_k\) 都满足这样一个性质。
\(n \le 2 \times 10^5\) 。
题解
首先建出 \(\mathrm{SAM}\)。于是我们只要从 \(\mathrm{SAM}\) 的 \(\mathrm{fail}\) 树上从根节点往下的一条路径中选出尽量多的节点,满足上一个点所代表的子串在这一个点至少出现了 \(2\) 次。由于一个点所代表的若干个集合的 \(endpos\) 集合是相同的,因此我们可以直接取这个点代表的所有子串中,长度最长的子串。
考虑从上往下不断贪心选点,那么这一个点能否被选择,只取决于这一个点代表的最长子串,是否包含了上一个被选择的点所代表的子串至少 \(2\) 次。这里可以考虑用线段树合并,来维护 \(endpos\) 集合。于是我需要对 \(\mathrm{SAM}\) 上每个点多记录一个第一次扩展出当前节点的时间 \(id_i\)。这样就可以得到,这个节点所代表的字符串,对应原字符串的 \([id_i - len_i + 1,id_i]\) 这样一个区间。假如我要查询节点 \(u\) 所代表的最长字符串在节点 \(v\) 代表的最长字符串出现了多少次,那么我只需要在 \(u\) 这个节点的线段树上查询 \(endpos\) 位于 \([id_v - len_v + len_u,id_i]\) 这个区间内的和即可。
LOJ 2720 「NOI2018」你的名字
题意
给定一个模板串 \(S\) 。接下来会给出 \(m\) 个询问。每次询问给出询问串 \(T\) 和区间 \([l,r]\)。求 \(T\) 串有多少个本质不同的子串没有在 \(S\) 串的 \([l,r]\) 中出现过。
\(|S| \le 5 \times 10^5,m \le 10^5,\sum|T| \le 10^6\) 。
题解
考虑枚举 \(T\) 串的每一个前缀 \(T_{1,i}\),求出这个前缀与 \(S_{l,r}\) 这个串的每个后缀的 \(\text{LCP}\) 的 \(\max\),记为 \(pre_i\)。这样算答案的时候,我只需要枚举 \(T\) 串的 \(\mathrm{SAM}\) 上每一个节点,这个节点对于答案的贡献即为 \(len_i - max(len_{link_i},pre_{id_i})\)。其中 \(id_i\) 同样表示 \(i\) 节点第一次扩展出来的时间。
如何求 \(pre_i\) 呢?这个同样可以通过建出 \(S\) 串的 \(\mathrm{SAM}\) ,然后用线段树合并维护 \(endpos\) 集合。按照顺序枚举每个前缀,从 \(pre_{i - 1} + 1\) 开始尝试,记录包含当前这个长度的后缀在 \(S\) 串的 \(\mathrm{SAM}\) 上深度最浅的点的位置 \(u\),并且在线段树上查询以 \(u\) 为根的线段树上 \(endpos\) 位于 \([l + t,r]\) 这个区间内的点是否存在,其中 \(t\) 为当前尝试的长度。如果匹配失败,就减少这个匹配的长度并再次尝试,直到匹配成功或者匹配长度减少为 \(0\) 则退出。时间复杂度是 \(O((|S| + |T|) \log n)\) 的。
代码中最后统计贡献的时候对 \(0\) 取了 \(\max\) ,而且这个 \(\max\) 不取还会有问题,来解释一下原因。事实上克隆节点就相当于把某个节点的 \(\text{fail}\) 拆出来了,这样克隆节点的 \(len\) 就会小于其扩展出来的时间,而 \(pre_i\) 上界是 \(i\) ,因此可能会被减到负数。
LOJ 6041 事情的相似度
题意
给定一个长度为 \(n\) 的 \(01\) 串,并定义第 \(i\) 个前缀,表示从第 \(1\) 个字符到第 \(i\) 个字符组成的字符串。接下来有 \(m\) 次询问。每次询问会给出一个区间 \([l,r]\) ,查询第 \(l\) 个前缀到第 \(r\) 个前缀中,\(\mathrm{LCP}\) 最大的一对前缀的 \(\mathrm{LCP}\)。
\(n,m \le 10^5\) 。
题解
考虑建出这个串的 \(\mathrm{SAM}\),这样问题转化为,每次询问一个区间内的点中,所有点对的 \(\mathrm{LCA}\) 的 \(len\) 的最大值。注意到询问是可以离线的,因此考虑把询问按照右端点排序。
考虑如果我们维护出了每个节点的子树内出现时间最晚的点,姑且称作这个点的颜色,那么新加入一个点,从这个点到根节点的路径上,会经过若干段相同颜色的点,那么我只要在每一段深度最深的点处,在树状数组上修改一下。这个和 \(\mathrm{LCT}\) 的 \(access\) 操作是一样的,可以方便地用 \(\mathrm{LCT}\) 维护。查询的时候只需要在树状数组上查询即可。
当然直接在线也是有做法的。考虑用 \(\texttt{std :: set}\) 启发式合并维护 \(endpos\) 集合。每次新加一个数,产生贡献的肯定只会是这个数在 \(\texttt{set}\) 上的前驱后继。原因是,当前合并到这个区间,那么 \(\mathrm{LCA}\) 的 \(maxlen\) 是固定的,这样产生贡献的点对编号差距尽可能小,才可能对更多的询问贡献。这样维护完之后,只需要再做一次二维数点,统计一个区域内的最小值即可。树状数组维护 \(\max\) 的时候好像不太好删除,可以考虑把其中一维反过来,查询两维均小于等于某一个数的区域即可。
两种做法的复杂度都是 \(O(n \log^2 n)\) 的。
SA / SAM 题目集的更多相关文章
- Java高级程序员(5年左右)面试的题目集
		
Java高级程序员(5年左右)面试的题目集 https://blog.csdn.net/fangqun663775/article/details/73614850?utm_source=blogxg ...
 - 浙大版《C语言程序设计(第3版)》题目集 --总结
		
浙大版<C语言程序设计(第3版)>题目集 此篇博客意义为总结pta上浙大版<C语言程序设计(第3版)>题目集所做题目的错误点,心得体会. 1.练习2-10 计算分段函数[1] ...
 - KMP,Trie,AC自动机题目集
		
字符串算法并不多,KMP,trie,AC自动机就是其中几个最经典的.字符串的题目灵活多变也有许多套路,需要多做题才能体会.这里收集了许多前辈的题目做个集合,方便自己回忆. KMP题目:https:// ...
 - PTA数据结构与算法题目集(中文)  7-43字符串关键字的散列映射 (25 分)
		
PTA数据结构与算法题目集(中文) 7-43字符串关键字的散列映射 (25 分) 7-43 字符串关键字的散列映射 (25 分) 给定一系列由大写英文字母组成的字符串关键字和素数P,用移位法定义 ...
 - PTA数据结构与算法题目集(中文)  7-42整型关键字的散列映射 (25 分)
		
PTA数据结构与算法题目集(中文) 7-42整型关键字的散列映射 (25 分) 7-42 整型关键字的散列映射 (25 分) 给定一系列整型关键字和素数P,用除留余数法定义的散列函数将关键字映射 ...
 - PTA数据结构与算法题目集(中文)  7-41PAT排名汇总 (25 分)
		
PTA数据结构与算法题目集(中文) 7-41PAT排名汇总 (25 分) 7-41 PAT排名汇总 (25 分) 计算机程序设计能力考试(Programming Ability Test,简称P ...
 - PTA数据结构与算法题目集(中文)  7-40奥运排行榜 (25 分)
		
PTA数据结构与算法题目集(中文) 7-40奥运排行榜 (25 分) 7-40 奥运排行榜 (25 分) 每年奥运会各大媒体都会公布一个排行榜,但是细心的读者发现,不同国家的排行榜略有不同.比如 ...
 - PTA数据结构与算法题目集(中文)  7-39魔法优惠券 (25 分)
		
PTA数据结构与算法题目集(中文) 7-39魔法优惠券 (25 分) 7-39 魔法优惠券 (25 分) 在火星上有个魔法商店,提供魔法优惠券.每个优惠劵上印有一个整数面值K,表示若你在购买某商 ...
 - PTA数据结构与算法题目集(中文)  7-38寻找大富翁 (25 分)
		
PTA数据结构与算法题目集(中文) 7-38寻找大富翁 (25 分) 7-38 寻找大富翁 (25 分) 胡润研究院的调查显示,截至2017年底,中国个人资产超过1亿元的高净值人群达15万人.假 ...
 
随机推荐
- c++入门之—运算符重载和友元函数
			
运算符重载的意义是:将常见的运算符重载出其他的含义:比如将*重载出指针的含义,将<<与cout联合使用重载出输出的含义,但需要认识到的问题是:运算符的重载:本质仍然是成员函数,即你可以认为 ...
 - Unique Snowflakes UVA - 11572 (离散化+尺取法)
			
Emily the entrepreneur has a cool business idea: packaging and selling snowflakes. She has devised a ...
 - Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试
			
近来发现两个问题: 1.jenkins设置邮箱时邮件发送测试不成功,之前是成功的: 2.启动python服务时,使用8000端口报错,但是用其他端口可用: 百度了一下,原来是端口占用的问题,可用如下方 ...
 - 深入解读Promise对象
			
promise对象初印象: promise对象是异步编程的一种解决方案,传统的方法有回调函数和事件,promise对象是一个容器,保存着未来才会结束的事件的结果 promise对象有两个特点: 1.p ...
 - 谷歌浏览器报错 Active resource loading counts reached to a per-frame
			
Active resource loading counts reached to a per-frame limit while the tab is in background. Network ...
 - day 7-21 pymysql模块
			
一.安装的两种方法 第一种: #安装 pip3 install pymysql 第二种: 二.链接,执行sql,关闭(游标) import pymysql user = input("use ...
 - C# Note26: [MethodImpl(MethodImplOptions.Synchronized)]与lock机制
			
在进行.NET开发时,经常会遇见如何保持线程同步的情况.在众多的线程同步的可选方式中,加锁无疑是最为常用的.如果仅仅是基于方法级别的线程同步,使用System.Runtime.CompilerServ ...
 - JUnit测试提示Java.lang.Exception: No runnable methods
			
网上一大堆都说,没写@Test,或者是,导包错误,= =然而我并没有发现我有这个毛病 = =最后终于找到罪魁祸首 Junit版本太低!!! Junit版本太低!!! Junit版本太低!!! = =因 ...
 - Yii2后台管理系统常规单据模块最佳实践
			
后台管理系统的常规单据通常包括数据,页面,功能:其中数据,页面,功能又可以细分如下: 分类 二级分类 主要内容 注意事项 例如 数据 数据库迁移脚本 用于数据表生成及转态回滚 1.是否需要增 ...
 - mysql group by 对多个字段进行分组
			
在平时的开发任务中我们经常会用到MYSQL的GROUP BY分组, 用来获取数据表中以分组字段为依据的统计数据.比如有一个学生选课表,表结构如下: Table: Subject_Selection S ...