字符串相关 学习笔记
哈希和哈希表
令 $b$ 为选取的基数(229或233等),则字符串匹配时子串 $C’=c_{k+1}c_{k+2}\dots c_{k_n}$ 的哈希值为
$$ H(C’)=H(C,k+n)-H(C,k)\times b^n $$
哈希的相关运用
- 哈希字符串匹配
转化成上面的板子就好做了。
- 哈希找回文串
正着存一组哈希值,再倒着存一组哈希值。枚举回文串的对称中心,二分回文长度(的一半),利用哈希判等。可以在 $O(n\log n)$ 的复杂度内找最长的回文串、回文串的数量。
例题:P3501
KMP
运用两个指针 $i,j$ 表示以 $A[i]$ 结尾长度为 $j$ 的字符串正好与 $B$ 串的前 $j$ 个字符匹配。若下一个字符匹配,则挪动指针;否则将 $j$ 挪动到 $fail[j]$ 处。 $fail[j]$ 为失配数组,表示在 $B$ 串的 $j$ 处失配时, $j$ 应该往前跳到的位置。
首先对 $B$ 串预处理,得到 $fail$ 数组,预处理过程可视为 $B$ 串自己和自己匹配的过程。
该算法复杂度为线性的。
KMP的相关运用
- KMP求公共前后缀
在KMP中,若一个串长度为 $n$ ,则 $fail[n]$ 就是该串公共前后缀的长度。
- KMP求最大(最小)周期
如果一个长度为 $n$ 的串有长度为 $j$ 的公共前后缀,则该串必有长度为 $n-j$ 的周期。
由此,在KMP中,若一个串长度为 $n$ ,则 $fail[n]$ 是最长公共前后缀,则 $n-fail[n]$ 是该串的最小周期;在 $j>0$ 时找 $fail[j]$ ,最小的非0的 $j’$ 是最短公共前后缀,则 $n-j’$ 是该串的最大周期。
扩展KMP
求解的问题:求出一个字符串的每个后缀与一个字符串(可以是自己)的最长公共前缀。
先暴力求得 $Z_1$ 的值,然后向后枚举。像后文的 manacher 一样维护 $pos,maxr$,并判断 $Z_{i-pos}$ 能否扩展到 $maxr$ 右边,如果不能就直接更新 $Z_i$,否则暴力更新。
失配树
求解的问题:求出一个字符串的两个前缀的最长公共前后缀。
先跑KMP,容易发现,一个字符串的最长公共前后缀就是 $fail[n]$。同时 $fail[fail[n]]…$ 等都是他的最长公共前后缀。那么我们容易知道,两个字符串的公共前后缀构成树形。于是我们将 $(fail[i],i)$ 连边,会得到一棵树,这就是失配树。上面那个问题可以在失配树上求LCA解决。
AC自动机
若有一个主串需要与多个模式串匹配,需要用的AC自动机。考虑将多个模式串建立字典树,再在字典树上建立 $fail$ 数组,即可完成匹配。
具体实现:先建立字典树,注意下标从1开始。然后将0号结点的所有边全部连向1号结点。从1开始进行广搜,对于每个结点 $u$,枚举所有出边,若不存在,则直接将出边连向 $fail[u]$ 的出边;否则将儿子入队,找到 $fail[u]$ 能跳到的有和该儿子相同的结点,更新 $fail[v]$。
1 | void init(){ |
AC自动机相关题目处理技巧
可以先考虑只有一个模式串的情况,先按KMP的做法写,思路厘清后再甩到字典树上。
最小表示法
参考这个。思路和代码都比较简单。
1 | int solve(){//返回最小表示的初始位置 |
manacher算法
先在两个字符间插入一个特殊字符,然后执行以下步骤:
1.如果 $i<maxr_i$,按对称方法更新 $p_i$。
2.暴力拓展 $p_i$。
3.更新 $maxr$ 和 $pos$。