字符串小记

12.31:写了 50%,还有很多东西还在施工中,最近 p 事太多了,呜。

字符串小记

字符串题目准则

在 OI 领域,做题尽量使用难度较低的字符串算法,使用难度较高的算法可能适得其反。

SAM

一个一直想学却鸽子的东西,有无数的人讲了无数次,但是我才第一次真正了解它。

https://yutong.site/sam/ 画图

SAM 入门,前人之述备矣。

我一开始读的 OI-wiki,前面还行,但是最后算法构造那里晕乎乎的,不建议。

https://www.cnblogs.com/zaza-zt/p/15419181.html

https://www.luogu.com.cn/article/v6v0kfpa

这两我觉得都很好,我主要读的第一篇。

广义 SAM (多串 SAM):

https://www.cnblogs.com/Xing-Ling/p/12038349.html

https://www.luogu.com/discuss/322224

广义 SAM 有多种写法,我采用 Bfs 的做法,优势是没有 corner,没有细节(就是套了个 trie)劣势是空间常熟偏大,不能在线加字符串。

几点共识:

后缀树是反串的 SAM 的 parent 树 ( link 树),所以并不用去学习后缀树算法。

在 99.99% 的题目中 SAM 的函数内部构成完全一样(广义 SAM 同理),所以不了解底层逻辑无伤大雅(比如复杂度证明之类的)

SAM 有用的性质(有很多,只列举我会的且常用的)

1.本质不同子串:\(\sum_i len_i-len_{fa_i}\)

1.广义 SAM 上每个模板串包含的节点数量和的上界以及构造

2.快速定位子串,求 区间 \([l,r]\),在 SAM 上的对应状态:在构建 SAM 时容易预处理 \(s_{1,i}\) 所表示的状态 \(pos_i\)。从 \(pos_r\) 开始在 link 树上倍增找到最浅的,len 值 ≥r−l+1 的状态 p 即为所求。

3.两个子串的 LCS,定位子串之后的 link 树上的 LCA 的 len

SAM 例题:

https://www.cnblogs.com/Meatherm/p/18221137#广义-sam-部分

写得很详细很清楚,点赞。

总结一下就是 SAM 的用法大概是:

1.利用定义做题,例如等价类所对应的是一段区间,len 以及 endpos 大小之类的直接对应题目所求的东西,那么把 SAM 拿出来处理信息就好了。

2.利用自动机的特性做匹配问题,SAM 也是自动机,和 AC 自动机一样也是做字符串匹配的高手,

模板:https://www.luogu.com.cn/problem/SP1812

这类题大概就是在 SAM 走路,然后顺带维护点信息。

3.建立反串后缀树,刻画前缀信息,许多字符串题都与 LCP 有关,而 SAM 全在刻画后缀信息,反过来建立,这类题大多时候可以平替 SA。

https://www.luogu.com.cn/problem/P4248

4.多串结构,把一个串的问题套到多个串上,增加维度,使用广义 SAM 就好了,大多数时候的分析根一个串类似。

https://www.luogu.com.cn/problem/P3346

5.维护 endpos 集合,有些题目并不是只需要 endpos 集合大小,需要具体信息,利用 线段树合并 维护出每个节点的集合,然后在上面套数据结构查询。

https://www.luogu.com.cn/problem/CF1037H

6.DS 嵌套,大概是利用 SAM 的一些树上性质转化为 DS 语言,再嵌套上 DS。

例题:https://www.luogu.com.cn/problem/P4482

把 border 转化成 SAM 语言就是找到一个最大的 \(l\le p <r\)\(p\) 使得 \(p-lcs(p,r)<l\),然后 lcs 的刻画上面有些,然后转树分治,细节不展开。

注意:一定要开两倍空间 !!!

想做更多题可以去看老麦的课件,里面还是有很多好题的。

KMP

算是字符串的精髓了把。

算法流程:

维护当前前缀的最长 border,那么在字符串匹配的时候失配了直接跳 border,均摊复杂度正确。

应用题:https://www.luogu.com.cn/problem/P4696

重新定义 KMP 的相等,考虑 KMP 匹配过程,要维护数之间的相对顺序,那么每次新匹配一个数的时只需要满足一个串的偏序(大小)关系在另一个串里也适用即可,维护下标在它之前的,它的前驱下标后继下标。若新插入的值大于前驱对应的值小于后继的值那么两个串就相等。

持久化:https://codeforces.com/problemset/problem/1721/E

维护单串 ACAM 就好了。

Border Theory

周期引理:

对于字符串 \(s\)\(p,q\)\(s\) 的周期,\(p+q-\gcd(p,q)\le |s|\),则 \(\gcd(p,q)\) 也是 \(s\) 的周期。

有事去掉左部分的 \(\gcd(p,q)\) 更易于证明一些性质。

将所有 Border 按其二进制最高位进行分段,每一段内的 Border 都构成一个等差数列,也就是至多划分为 \(\log |S|\) 个等差数列。

由于等差数列是一段连续的 border,暴力跳即可找到所有等差数列。

证明:https://www.cnblogs.com/y-dove/p/14514097.html

用法:

1.找到 log 个等差数列然后辅助数据结构转移

https://www.luogu.com.cn/problem/P1393 减去算重的贡献前缀差分就好了。

练习:https://www.luogu.com.cn/problem/P4156

2.利用周期的性质加速加速跳 border

https://www.luogu.com.cn/problem/P5287

考虑暴力跳 kmp 的 border 是均摊的,导致带撤销复杂度假了。

考虑利用周期的性质,显然对于周期 \(d\),周期 \(2d\),…… \(kd\),根据周期引理,对于所有小于 \(n-d\) 的周期都是 \(d\) 的倍数,所以跳 border 的时候只用检查 \(d,2d\) (其他周期的下一个位置都和 \(2d\) 一样,然后直接跳到 \(kd\) 转移就好了)。

ACAM

即多串 KMP,在 trie 树上维护 fail 树,是做匹配的一把好手。

用法:

1.fail 树上乱跳,统计贡献,有若干模板题,不再一一列举了。

练习:SCOI2024d1t1(缺数据),ACAM 综合应用。

2.根号分治,我感觉是 ACAM 用得最多的地方,由于输入量有限,串总长度和一定,自然可以根号分治。

例题:https://www.luogu.com.cn/problem/P8571

做法大概就是憋两种本质不同的做法,合并一下就好了。

长串一般就是暴力做,配个数据结构快速查询。

短串一般就是建个虚树,在上面分析一下就好了。

这种题一般还是代码难度更大。

练习题:https://www.luogu.com.cn/problem/P8203,这种题最后难度应该也不在 ACAM 上。

吃屎题:https://www.luogu.com.cn/problem/P9997 有兴趣可以写。

匹配相关杂项

众所周知,我是懒人,不想写打字符串结构怎么办。

匹配相关有两个有趣的套路。

bitset

大概思路就是你优化一个一个匹配的过程,开个 bitset 维护所有合法 endpos,每次遇到一个新的字符就暴力 and 一下,可以配合代码理解。

https://www.luogu.com.cn/problem/CF914F

FFT

有些带通配符的匹配时候,设计一个合理的相等函数。

https://www.luogu.com.cn/problem/P4173

扩展题:https://www.luogu.com.cn/problem/CF827E,还需要一些周期相关的性质。

哈希

核心思想就是将一个很长很大的东西映射到一个数上做比较。

自然溢出是好文化,但是有些人就喜欢卡。

原理:https://www.cnblogs.com/tzcwk/p/hash-ull-hack.html

二维哈希:记得不同维度使用不同的 base,不然容易挂,测试:https://www.luogu.com.cn/problem/UVA11019

练习题:https://www.luogu.com.cn/problem/CF1968G2

回文相关

着急学习的可以看老麦博客。

绝赞更新中,笔者正在学习相关内容,欢迎催更。

posted @ 2024-12-18 15:43  Hanghang007  阅读(54)  评论(6编辑  收藏  举报