【学习笔记】字符串
字符串下标从
kmp
在匹配一个模式串
得到
复杂度线性,每一次操作最多只会让
与文本串的匹配类似把文本串接到模式串后面再次进行 kmp,具体做法类似甚至相同。
manacher
经典 trick:给每两个字符中间插一个特殊符号,此时原字符串中的所有回文都是奇长度回文。设
记录当前扩展的最右边界
为什么说是可能,因为
然后暴力扩展更新即可。
复杂度是线性的,因为如果需要进行暴力扩展的操作,每一次扩展都会使得结束后
exkmp/Z 函数
感觉相比 kmp 更像 manacher?
同样考虑
维护一个
如果当前
但是
最后你暴力扩展得到最后的
复杂度分析和 manacher 一样都是线性。与另一个模式串的匹配可以参考 kmp,相当于直接把新串接到原串后面继续求。
AC 自动机(ACAM)
应对很多模式串在文本串的匹配。
如果是单个串的匹配就是使用 kmp 完成,同样地考虑记录每个串第
以下简称
假如说我们在第
如果
在更新答案的时候把从自己往前的所有
PAM
感觉比 manacher 好用。考虑和 AC 自动机相同的构造方法。对于每个节点维护
奇偶问题就直接开两个树一个奇一个偶,连 fail 的时候把偶根连到奇根上来,原因是单独的一个字符也是回文所以奇根永远不会失配。然后 PAM 的每一个点都代表一个回文串。
以
判断什么时候失配需要保存当前回文串的长度然后推出来自己对面的是哪个字符。所以有
还是很板子的。注意更新 fail 要在建点之前。不然会出现自己的 fail 连到自己身上然后死循环。
有一个结论是长度为
证明:
-
对于
,显然正确。 -
考虑新添加一个字符,新增的回文串一定以它为右端点。令构成的最长的回文串左端点为
,当前为 ,中点是 ,其他更小的回文串的左右端点和中点为 。 -
那么对于
的所有回文串,它们没有跨过中点,根据回文的对称在对面一定出现过,也就是不是一个新串。 -
对于
的回文串,它有一部分跨过了 ,令这一部分为 ,根据对称性最大的回文串以 为回文,左边是 右边应该是 的倒串。也就是说你把右半边倒到左半边来一定能和右边的 的倒串连出一个一模一样的回文串。 -
也就是说每一次最多只会多一个本质不同的回文串,而且是以自己为右端点能够构成的最大回文串。
复杂度证明:
-
考虑
while
循环外一次都是 的,看跳 fail 的函数中while
执行的次数。 -
容易看出每跳一次当前点的深度至少减
。 -
单个字符插入完之后深度只会固定加
。 -
而深度归
后不可能继续往上面跳了,所以复杂度是 的。
SA
定义编号为
使用倍增合并两个当前的后缀,每次合并完重新排一次序,得到最后的后缀数组,比较的时候先比较前面的大小,把后面接上来作为第二关键字。
这里可以用到基数排序,即用桶把出现的东西记下来之后做前缀和,这样就大致知道每个数的排名。然后从后往前推每个数的排名,每次把对应的桶值减一,这样就不会有重复。注意这种排序是稳定排序。
我们可以一开始直接构建一个以第二关键字为排序的第一关键字数组,因为第二关键字原本就是排好序的。这样重新用基数排序就可以得到原来的以开头段为关键字,接上来那段的比较也在一开始就提了出来经过稳定的基数排序得到了正确的顺序。
复杂度
用 SA 的题基本上都会用到
于是我们在知道
求法的话,结论是
证明(照搬自 OI-WIKI):
-
如果
直接成立。 -
所以
。令这个前缀是 ,其中 是单个字符, 非空。 -
根据
的定义 。因为后面那个排名比 小,是 。所以令 代表 , 代表 。其中 可为空, 非空。 -
那么后缀
就是 ,并且存在后缀 为 。 -
由于
大小关系紧接着 也就是后缀 ,那么 后缀 而且中间没有其他字符串。 -
又因为
,所以 后缀 。也就是说有一个公共前缀 。回过头来看 代表的是 减去前面的单个字符 ,即 。 -
所以
,化成结论中的式子,得证。
额每一个后缀的所有前缀都是字符串的一个不重不漏的子串,应该只有我觉得这个不是一眼得出来的吧。
SAM
给定字符串
除空间外薄沙 SA,同时据 wyb 讲通过哈希能够把 SAM 空间也开成
构建出来的 SAM 是一个 DAG,称点为节点/状态,边为转移。那么我们构建的 SAM 状态上限为
SAM 有一个源点
SAM 中存在一个或多个终止状态。终止状态满足从初始状态到终止状态的转移连起来一定是原串的一个后缀。
从初始状态到任意一个状态结束的路径的转移连起来都是原串的子串。
一个状态可能表示不同的子串。令一个串在
定义一个节点的后缀链接
再定义一个节点包含的最长串长度为
ok 然后读一遍求法。SAM 为增量构造,每次增加一个字符
令
好我们假设存在
好那么只剩
结束前记得更新
SAM 有一些比较优秀的性质,这让其可以处理非常多的字符串问题。
一个状态包含的子串数为
根据子串和转移的相互转化,不同子串的个数相当于 SAM 中的路径数,即为经典 DAG 路径计数问题。
一个串的出现次数即为
定义一个串的第一个出现位置为
广义后缀自动机
普通的 SAM 只能处理一个串,所以加强一下处理多个串的问题。得到广义后缀自动机!
广义 SAM 的做法把这些串挂在 trie 上面然后根据 trie 构造广义 SAM。
据说网上流传非常多的假的广义 SAM,但是板子题要输出广义 SAM 的点数会卡掉很多正确性不对的做法。其他做法可能复杂度会出锅。反正抄的题解区第一篇总不会错太多吧。
离线
相比在线较为简单。但是在某些题目里面不如在线好维护一些东西,具体因题而异。
把 trie 先挂好。插入字符
因为要知道父亲的状态所以插入完之后返回一个值表示这个字符插入完之后广义 SAM 的状态位置。也就是自己子节点插入时需要的
trie 自动帮忙把 lcp 去掉了,所以感觉正确性是有的。但是其实是不知道正确性的。
在线
相比于离线可以更好地统计单独一个母串的一些贡献(?。
这种做法不需要把 trie 建立出来。每次把新串插入到现在建的 SAM 上,然后一个串插完之后把
但是这种做法是错误的。已经有很多方法能够卡调掉这种做法(吗?。所以我们需要加一些特判。据题解区说这个做法经过大量对拍也没有出错,于是这是正确的做法。
如果添加
如果拆节点时拆的是
发现出现问题的地方都是在
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具