字符串笔记
字符串笔记
串串能吃吗?
本文我尝试重新从头开始理解字符串。由此可见,难度梯度可能较大。
发现在谐音替换、摩卡串中取得了 \(0+0=0\) 的好成绩。
字符串中很多题目往往可以有多种做法,所以可能会在不同的章节出现多次。在本文中,未加说明的 \(n\) 表示 \(|S|\)。
由于内容较多,全部放在同一篇博文中容易造成卡顿,会分为若篇。
基础内容
本部分以概念为主,主要参考 OI-wiki 字符串基础,注意该页面 0-index 和 1-index 的区分。
字符集
字符集,符号为 \(\Sigma\)。是一个全序关系(可视为 \(\le\) )的集合。也就是说,\(\forall x,y \in \Sigma,x \neq y\),\(x<y\) 和 \(x>y\) 有且仅有一个成立。将所有关系 \(x \lt y\) 连一条 \(y \to x\) 的有向边,得到的一定是一张无环的竞赛图。
在算法竞赛中字符集一般为小写字母集,即 \(\Sigma = \{\texttt{a},\texttt{b},\dots,\texttt{z}\},\texttt{a} < \texttt{b} < \dots < \texttt{z}\)。
字符集的全序要求是为了字典序的定义。在一些场景中,我们可能会忽略全序性。
字符串
由若干个字符 \(c,c\in \Sigma\) 依次排列的序列,叫做字符串。
和一般的序列一样,字符串可以下标从 \(1\) 开始,亦可以下标从 \(0\) 开始。本文默认从 \(1\) 开始。
\(|S|\) 表示字符串 \(S\) 的长度,即字符串的字符数。\(S_i\) 表示字符串 \(S\) 的第 \(i\) 个字符。
空串是一类特殊的字符串,记为 \(\varepsilon\),\(|\varepsilon|=0\)。
字符串的相等定义为两串长度相等且相同位置字符相同。即:\(S=T \Leftrightarrow |S|=|T|,\forall 1 \le k \le |S|,S_k=T_k\)。
定义字符串拼接,记作 \(ST\) 或 \(S+T\)(\(S,T\) 为字符串)得到一个字符串 \(S_1S_2\dots S_{|S|}T_1T_{2}\dots T_{|T|}\)。记 \(S^n\) 为 \(\begin{matrix}\underbrace{S\cdots S}\\n \text{个} S\end{matrix}\),即将 \(n\) 个字符串依次拼接起来。
子串与子序列
字符串 \(S\) 的子串 \(S_{[l\dots r]}\) 表示由 \(S_l,S_{l+1},\dots,S_{r}\) 顺次拼接而成的字符串。[1]
字符串 \(S\) 的子序列定义为将若干元素提取出来并不改变相对位置形成的序列,即 \(S_{p_1},S_{p_2},\dots,S_{p_k}\) 顺次拼接而成,且 \(1 \le p_1 \lt p_2 \lt \dots \lt p_k \le n\)。[1:1]
简单的说就是子串要求在原串中必须连续,子序列不要求。
前缀与后缀
前后缀为两类特殊的子串。前缀为 \(S_{[1,i]}\),后缀为 \(S_{[i,n]}\)。[1:2]
字典序
定义字符串的比较关系。
称字符串 \(S<T\) 当且仅当满足以下两个条件之一:
- 存在 \(i \in [1,\min \{|S|,|T|\}] \cap \mathbb Z\),满足 \(S_i < T_i\) 且 \(S_{[1,i-1]}=T_{[1,i-1]}\);
- \(|S|<|T|\),且 \(S\) 为 \(T\) 的前缀。
字典序的 \(<\) 为严格全序关系。
回文串
回文串为从前往后和从后往前读完全一样的串。形式化地,\(S_{i}=S_{|S|-i+1}\)。
一些常用的缩写
LCP(Longest Common Prefix):指最长公共前缀。类似的有 LCS,最长公共后缀。
字符串哈希
字符串哈希是一类相当通用的算法。[2]
模板:P3370 【模板】字符串哈希 - 洛谷,更强的数据:U461211 字符串 Hash(数据加强)- 洛谷
可以发现,比较两个字符串是否相等是 \(\mathcal O(|S|)\) 的。
而哈希的思想就是将很大的集合范围映射到一个较小的值域范围。所以该映射一般为单射,基本不可能做到双射。
什么叫“小的值域范围”,对于哈希表而言为空间允许的范围,而对于字符串哈希主要是为了比较两字符串是否相等,在 64/32 位整数下就可以了。
一般字符串哈希采用多项式哈希。即
其中式中的 \(S_i\) 常是用 \(S_i\) 在字典序中的排名,且空出 \(0\) 不用,避免 \(\texttt{a}\) 和 \(\texttt{aa}\) 无法区分。
而 \(b\) 和 \(P\) 这两个参数的取值则影响哈希的正确性,常见的 \(P\) 取 \(\sim 10 ^9\) 的大质数或者直接取 \(2^{64}\) 自然溢出,但均可以 Hack(Hash Killer I/II/Extra)。
所以实践中常使用双哈希,但也不是万能的:Hash Killer Phantasm。
但是,未知 \(b\) 和 \(P\) 的双哈希暂时未被 Hack,Hash Killer III【Open Problem】。
在实践中由于随机大质数有些困难,随机底数即可。在 CodeForces 的赛制下这很重要。
而在其它问题中,我们往往需要求一个子串的哈希值。可以发现,哈希值是可差分的,这就说明,我们在求出前缀哈希值后可以通过差分求出任意子串的哈希值。具体地:
KMP 与 Border 理论
有限状态自动机
参考资料
- OI-wiki - 字符串
- 题解 P4482 【[BJWC2018]Border 的四种求法】
- Border理论小记
- 国家集训队2021论文集 徐哲安《浅谈有限状态自动机及其应用》,可以在OI-wiki/libs Github获得

浙公网安备 33010602011771号