符号与约定
在本文中:
字符串索引默认从 1 1 1 开始。
用 s [ l , r ] s_{[l, r]} s [ l , r ] 表示子串 s l s l + 1 s l + 2 … s r s_l s_{l + 1} s_{l + 2} \ldots s_r s l s l + 1 s l + 2 … s r ,特别地,当 [ l , r ] = ∅ [l, r] = \varnothing [ l , r ] = ∅ 时表示空串。
用 p r e ( s , i ) \mathrm{pre}(s, i) p r e ( s , i ) 表示 s [ 1 , i ] s_{[1, i]} s [ 1 , i ] ,s u f ( s , i ) \mathrm{suf}(s, i) s u f ( s , i ) 表示 s [ i , ∣ s ∣ ] s_{[i, |s|]} s [ i , ∣ s ∣ ] 。
定义
对于一个字符串 s s s ,若正整数 i < ∣ s ∣ i<|s| i < ∣ s ∣ 满足 p r e ( s , i ) = s u f ( s , ∣ s ∣ − i + 1 ) \mathrm{pre}(s, i) = \mathrm{suf}(s, |s| - i + 1) p r e ( s , i ) = s u f ( s , ∣ s ∣ − i + 1 ) ,则称 p r e ( s , i ) \mathrm{pre}(s, i) p r e ( s , i ) 为 s s s 的一个 Border。下文用 B ( s ) \mathcal{B}(s) B ( s ) 表示 s s s 的所有 Border 构成的集合。
对于一个字符串 s s s ,若正整数 k < ∣ s ∣ k < |s| k < ∣ s ∣ 满足 ∀ i ∈ ( k , ∣ s ∣ ] , s i = s i − k \forall i \in (k, |s|], s_i = s_{i - k} ∀ i ∈ ( k , ∣ s ∣ ] , s i = s i − k ,则称 k k k 为 s s s 的一个周期(Period),特别地,若 k ∣ ∣ s ∣ k \mid |s| k ∣ ∣ s ∣ ,则称 k k k 为 s s s 的一个整周期。下文用 P ( s ) \mathcal{P}(s) P ( s ) 表示 s s s 的所有 Period 构成的集合。
例如:对于字符串 abcab \texttt{abcab} abcab ,ab \texttt{ab} ab 是它的一个 Border,且它有一个 3 3 3 的周期。
定理 1 :对于一个字符串 s s s ,若用 t t t 表示其最长的 Border,则有 B ( s ) = B ( t ) ∪ { t } \mathcal{B}(s) = \mathcal{B}(t) \cup \{t\} B ( s ) = B ( t ) ∪ { t } 。
也就是对于一个串,除了最长的 Border 以外,其他的 Border 都是最长 Border 的 Border。
证明就是考虑根据定义可以得到对于 s s s 的任意一个除 t t t 以外的 Border,都一定是 t t t 的 Border。
稍微手玩一下就行,读者自证不难。
定理 2 :一个字符串的 Border 与 Period 一一对应。具体地,p r e ( s , i ) ∈ B ( s ) ⟺ ∣ s ∣ − i ∈ P ( s ) \mathrm{pre}(s, i) \in \mathcal{B}(s) \iff |s| - i \in \mathcal{P}(s) p r e ( s , i ) ∈ B ( s ) ⟺ ∣ s ∣ − i ∈ P ( s ) 。
证明考虑分讨 i ≤ ∣ s ∣ 2 i \le \frac{|s|}{2} i ≤ 2 ∣ s ∣ 和 i > ∣ s ∣ 2 i > \frac{|s|}{2} i > 2 ∣ s ∣ 两种情况即可。
前缀函数与 KMP 算法
根据性质 1,我们只需要知道一个串所有前缀的最长 Border,就可以求出该串的所有 Border。
对于一个字符串 s s s ,我们定义其前缀数组为一个长度为 ∣ s ∣ |s| ∣ s ∣ 的数组 π \pi π 。其中 π i \pi_i π i 为 p r e ( s , i ) \mathrm{pre}(s,i) p r e ( s , i ) 最长 Border 的长度,特别地,若该前缀没有 Border,则 π i = 0 \pi_i = 0 π i = 0 。
考虑在线地求出前缀函数,也就是根据 π 1 … π i − 1 \pi_1 \ldots \pi_{i - 1} π 1 … π i − 1 去求出 π i \pi_i π i 。
首先容易发现,π i ≤ π i − 1 + 1 \pi_i \le \pi_{i - 1} + 1 π i ≤ π i − 1 + 1 。更进一步地,当 π i > 1 \pi_i > 1 π i > 1 时,我们有 p r e ( s , π i − 1 ) ∈ B ( p r e ( s , i − 1 ) ) \mathrm{pre}(s, \pi_i - 1)\in\mathcal{B}(\mathrm{pre}(s, i - 1)) p r e ( s , π i − 1 ) ∈ B ( p r e ( s , i − 1 ) ) 。根据这一点,我们得到了一个“朴素的暴力”:每次从 π i − 1 \pi_{i - 1} π i − 1 开始从大到小遍历 B ( p r e ( s , i − 1 ) ) \mathcal{B}(\mathrm{pre}(s, i - 1)) B ( p r e ( s , i − 1 ) ) ,记当前遍历到的长度为 j j j ,若能向后扩展,即 s j + 1 = s i s_{j + 1} = s_i s j + 1 = s i ,则有 π i = j + 1 \pi_{i} = j + 1 π i = j + 1 。否则若遍历完所有 Border 还是找不到满足条件的 j j j ,则 π i = [ s 1 = s i ] \pi_i = [s_1 = s_i] π i = [ s 1 = s i ] 。
考虑分析这个算法的时间复杂度,用一个指针维护当前最后一个位置的 π \pi π ,发现每次做下一个位置时指针最多增加 1 1 1 ,所以这个算法的时间复杂度是线性的。
参考代码:
1 2 3 4 5 for (int i = 2 , j = 0 ; i <= n; i++) { while (j && s[j + 1 ] != s[i]) j = pi[j]; if (s[j + 1 ] == s[i]) pi[i] = ++j; }
单模式串字符串匹配
这是一个经典问题,即给定两个字符串 s , t s, t s , t ,求 t t t 在 s s s 中所有出现的位置。其中出现是指 t t t 与 s s s 的一个子串相等,出现的位置就是这个子串的位置。
解决这个问题的朴素想法是枚举每个位置,然后将这个位置的子串与 t t t 进行比较。
1 2 3 4 5 6 7 8 for (int i = 1 ; i + m - 1 <= n; i++) { int flag = 1 ; for (int j = 1 ; j <= m; j++) if (s[i + j - 1 ] != t[j]) {flag = 0 ; break ;} if (flag) printf ("%d " , i); } puts ("" );
考虑优化这个暴力,去除无用的枚举。
考虑设当前起点为 i i i ,在 s s s 的位置 j + 1 j + 1 j + 1 失配时,我们想要找到一个最小的位置,使得其至少在 j + 1 j + 1 j + 1 失配。
形式化地,我们需要求一个最大的 l l l ,使得 j − l + 1 > i , p r e ( t , l ) = s [ j − l + 1 , j ] j - l + 1 > i, \mathrm{pre}(t, l) = s_{[j - l + 1, j]} j − l + 1 > i , p r e ( t , l ) = s [ j − l + 1 , j ] 。
我们知道 s [ i , j ] = p r e ( t , j − i + 1 ) s_{[i, j]} = \mathrm{pre}(t, j - i + 1) s [ i , j ] = p r e ( t , j − i + 1 ) ,因此等价于要求 p r e ( t , l ) = t [ j − i + 1 − l + 1 , j − i + 1 ] \mathrm{pre}(t, l) = t_{[j - i + 1 - l + 1, j - i + 1]} p r e ( t , l ) = t [ j − i + 1 − l + 1 , j − i + 1 ] ,由此可得我们要求的 l l l 实际上就是 p r e ( t , j − i + 1 ) \mathrm{pre}(t, j - i + 1) p r e ( t , j − i + 1 ) 的最长 Border 的长度。
1 2 3 4 5 6 7 8 9 for (int i = 1 , j = 0 ; i <= n; i++) { while (j && t[j + 1 ] != s[i]) j = pi[j]; if (t[j + 1 ] == s[i]) if (++j == m) { j = nxt[j]; } }
容易发现 j j j 每次至多增加 1 1 1 ,因此时间复杂度是 O ( n ) O(n) O ( n ) 的。
其实还可以从另一个角度考虑这个问题,我们讲 s s s 和 t t t 拼起来得到 t + # + s t + \# + s t + # + s ,其中 # \# # 是一个不在 t t t 和 s s s 的字符集内的特殊字符,然后求它的前缀函数,发现匹配的位置就是在 s s s 部分 π = ∣ t ∣ \pi = |t| π = ∣ t ∣ 的位置。
因为 π i ≤ ∣ t ∣ \pi_i \le |t| π i ≤ ∣ t ∣ ,所以实际上不需要记录 s s s 部分的 π i \pi_i π i 的具体值,因此只需要在 t t t 上跑 KMP,然后用一个指针遍历 s s s ,另一个指针维护当前的 π i \pi_i π i 即可。写出来的码和上面是一样的。
失配树
考虑对于字符串的每个前缀建一个点,并对每个点 i i i 向 π i \pi_i π i 连一条边,可以发现这样得到了一棵内向树,我们称之为失配树。
对于每个前缀,其 Border 就是它在树上的所有非根祖先。
例题:CF1286E Fedya the Potter Strikes Back
给定一个字符串 S S S 和一个序列 W W W ,初始为空。
有 n n n 次操作,每次操作在 S S S 末尾添加一个字符并在 W W W 末尾添加一个数。并询问:
∑ S [ L , R ] = S [ 1 , R − L + 1 ] min i = L R W i \sum\limits_{S_{[L, R]} = S_{[1, R - L + 1]}} \min_{i = L}^R W_i
S [ L , R ] = S [ 1 , R − L + 1 ] ∑ i = L min R W i
强制在线。
1 ≤ n ≤ 6 × 1 0 5 1 \le n \le 6\times 10^5 1 ≤ n ≤ 6 × 1 0 5 。
TL: 4s, ML: 250MB.
只需考虑新串的所有 Border,再求前缀和即可。
容易发现新串的 Border 要么是原串的某个 Border 后面添加一个字符,要么长度为 1 1 1 。也就是说每次只会新增至多一个 Border,只要能实现快速删除不合法的 Border 和更新答案这题就做完了。
考虑如何删除不合法的 Border,只需要对于每个前缀记录其第一个后继不同的祖先即可。
然后考虑更新答案,用一个 map
记录答案为某个值的区间的数量然后暴力推平,时间复杂度与颜色段均摊相同。
然后这题就做完了,时间复杂度为 O ( n log n ) O(n\log n) O ( n log n ) 。
弱周期引理与周期引理
弱周期引理(Weak Periodicity Lemma) :
∀ p , q ∈ P ( s ) , p + q ≤ ∣ s ∣ ⟹ gcd ( p , q ) ∈ P ( s ) \forall p, q\in\mathcal{P}(s), p + q \le |s| \implies \gcd(p, q)\in\mathcal{P}(s)
∀ p , q ∈ P ( s ) , p + q ≤ ∣ s ∣ ⟹ g cd( p , q ) ∈ P ( s )
证明:
令 p < q p < q p < q ,设 d = q − p d = q-p d = q − p ,只需证 d ∈ P ( s ) d \in \mathcal{P}(s) d ∈ P ( s ) ,就可以用辗转相减法证明结论。
∀ i ∈ ( d , ∣ s ∣ − p ] , s i = s i + p = s i + p − q = s i − d \forall i \in (d, |s| - p], s_i = s_{i + p} = s_{i + p - q} = s_{i - d} ∀ i ∈ ( d , ∣ s ∣ − p ] , s i = s i + p = s i + p − q = s i − d
∀ i ∈ ( q , ∣ s ∣ ] , s i = s i − q = s i − q + p = s i − d \forall i \in (q, |s|], s_i = s_{i - q} = s_{i - q + p} = s_{i - d} ∀ i ∈ ( q , ∣ s ∣ ] , s i = s i − q = s i − q + p = s i − d
因为 p + q ≤ ∣ s ∣ p + q \le |s| p + q ≤ ∣ s ∣ ,即 q ≤ ∣ s ∣ − p q \le |s| - p q ≤ ∣ s ∣ − p ,因此 ( d , ∣ s ∣ − p ] ∪ ( q , ∣ s ∣ ] = ( d , ∣ s ∣ ] (d, |s| - p] \cup (q, |s|] = (d, |s|] ( d , ∣ s ∣ − p ] ∪ ( q , ∣ s ∣ ] = ( d , ∣ s ∣ ] 。故 d ∈ P ( s ) d \in \mathcal{P}(s) d ∈ P ( s ) 。
□ \square □
一般情况下弱周期引理就够用了,但实际上有更紧的界。
在讲周期引理之前插入一个小定理:
定理 3 :若字符串 t t t 是字符串 s s s 的前缀,且 a ∈ P ( s ) , b ∈ P ( t ) , b ∣ a , ∣ t ∣ ≥ a a \in \mathcal{P}(s), b \in \mathcal{P}(t), b \mid a, |t| \ge a a ∈ P ( s ) , b ∈ P ( t ) , b ∣ a , ∣ t ∣ ≥ a ,且 b b b 是 t t t 的整周期,则有 b ∈ P ( s ) b \in \mathcal{P}(s) b ∈ P ( s ) 。
容易发现 a a a 是 t t t 的周期,并且由于 b ∣ a b \mid a b ∣ a ,就可以得到 b b b 是 s s s 的周期。
理解不了的可以尝试自己画个图。
周期引理(Periodicity Lemma) :
∀ p , q ∈ P ( s ) , p + q − gcd ( p , q ) ≤ ∣ s ∣ ⟹ gcd ( p , q ) ∈ P ( s ) \forall p, q\in\mathcal{P}(s), p + q - \gcd(p, q) \le |s| \implies \gcd(p, q)\in\mathcal{P}(s)
∀ p , q ∈ P ( s ) , p + q − g cd( p , q ) ≤ ∣ s ∣ ⟹ g cd( p , q ) ∈ P ( s )
证明:
令 p < q p < q p < q ,设 d = q − p d = q - p d = q − p 。
由于 ∀ i ∈ ( d , ∣ s ∣ − p ] , s i = s i + p = s i + p − q = s i − d \forall i \in (d, |s| - p], s_i = s_{i + p} = s_{i + p - q} = s_{i - d} ∀ i ∈ ( d , ∣ s ∣ − p ] , s i = s i + p = s i + p − q = s i − d ,因此 d ∈ P ( p r e ( s , ∣ s ∣ − p ) ) d \in \mathcal{P}(\mathrm{pre}(s, |s| - p)) d ∈ P ( p r e ( s , ∣ s ∣ − p ) ) 。
考虑归纳,假设已经证明周期引理对于 p r e ( s , ∣ s ∣ − p ) \mathrm{pre}(s, |s| - p) p r e ( s , ∣ s ∣ − p ) 成立。
因为 p + q − gcd ( p , q ) ≤ ∣ s ∣ p + q - \gcd(p, q) \le |s| p + q − g cd( p , q ) ≤ ∣ s ∣ ,两边同减 p p p 得到 p + ( q − p ) − gcd ( p , q − p ) ≤ ∣ s ∣ − p p + (q - p) - \gcd(p, q - p) \le |s| - p p + ( q − p ) − g cd( p , q − p ) ≤ ∣ s ∣ − p ,因此 gcd ( p , q ) ∈ P ( p r e ( s , ∣ s ∣ − p ) ) \gcd(p, q) \in \mathcal{P}(\mathrm{pre}(s, |s| - p)) g cd( p , q ) ∈ P ( p r e ( s , ∣ s ∣ − p ) ) 。
因为 2 p = p + q − ( q − p ) ≤ p + q − gcd ( p , q ) ≤ ∣ s ∣ 2p = p + q - (q - p) \le p + q - \gcd(p, q) \le |s| 2 p = p + q − ( q − p ) ≤ p + q − g cd( p , q ) ≤ ∣ s ∣ ,即 p ≤ ∣ s ∣ − p p \le |s| - p p ≤ ∣ s ∣ − p ,因此有 gcd ( p , q ) ∈ P ( p r e ( s , p ) ) \gcd(p, q) \in \mathcal{P}(\mathrm{pre}(s, p)) g cd( p , q ) ∈ P ( p r e ( s , p ) ) 。
由于 gcd ( p , q ) ∣ p \gcd(p, q) | p g cd( p , q ) ∣ p ,因此 gcd ( p , q ) \gcd(p, q) g cd( p , q ) 是 s [ 1 , p ] s_{[1, p]} s [ 1 , p ] 的整周期,根据定理 3 即可得到 gcd ( p , q ) ∈ P ( s ) \gcd(p, q) \in \mathcal{P}(s) g cd( p , q ) ∈ P ( s ) 。
□ \square □
其他定理
定理 4 :对于文本串 s s s 和模式串 t t t ,若 ∣ t ∣ ≥ ∣ s ∣ 2 |t| \ge \frac{|s|}{2} ∣ t ∣ ≥ 2 ∣ s ∣ ,且 t t t 在 s s s 中至少成功匹配了 3 3 3 次,则每次匹配的位置形成一个等差数列,且公差为 t t t 的最小周期。
证明:
考虑匹配成功的第 1 1 1 次、第 2 2 2 次和最后一次,分别记为 t 1 , t 2 , t x t_1, t_2, t_x t 1 , t 2 , t x 。
设 t 1 , t 2 t_1, t_2 t 1 , t 2 的间隔为 d d d ,t 2 , t x t_2, t_x t 2 , t x 的间隔为 a a a 。显然 d , a d, a d , a 都是 t t t 的周期。
因为 ∣ t ∣ ≥ ∣ s ∣ 2 |t| \ge \frac{|s|}{2} ∣ t ∣ ≥ 2 ∣ s ∣ ,所以 t 1 , t x t_1, t_x t 1 , t x 的间隔不会超过 ∣ t ∣ |t| ∣ t ∣ 。
所以 d + a ≤ ∣ t ∣ d + a \le |t| d + a ≤ ∣ t ∣ ,根据弱周期引理,gcd ( d , a ) \gcd(d, a) g cd( d , a ) 也是 t t t 的周期。
设 t t t 的最小周期为 p p p ,显然有 p ≤ gcd ( d , a ) ≤ d p \le \gcd(d, a) \le d p ≤ g cd( d , a ) ≤ d ,因此 p + gcd ( d , a ) ≤ d + gcd ( d , a ) ≤ d + a ≤ ∣ t ∣ p + \gcd(d, a) \le d + \gcd(d, a) \le d + a \le |t| p + g cd( d , a ) ≤ d + g cd( d , a ) ≤ d + a ≤ ∣ t ∣ ,根据弱周期引理显然还有周期 gcd ( p , d , a ) ≤ p \gcd(p, d, a) \le p g cd( p , d , a ) ≤ p 。因为 p p p 最小,故 p ∣ gcd ( p , d , a ) p\mid\gcd(p, d, a) p ∣ g cd( p , d , a ) ,因此 p ∣ d p\mid d p ∣ d 。
若 p ≠ d p\ne d p = d ,则在与 t 1 t_1 t 1 间隔 p p p 的位置也会有一个匹配,因此一定有 p = d p = d p = d 。
显然上面的分析中只要满足 t x t_x t x 是第 ≥ 3 \ge 3 ≥ 3 次匹配即可,因此相邻两次匹配的间隔一定都是 p p p 的倍数,并且除最后一次外每次匹配间隔 p p p 的位置一定也会有一个匹配。故所有匹配的间隔相同,即匹配的位置形成等差数列。
□ \square □
然而我并没有在 OI 中见到什么题目需要用到这个定理,如果有人知道请告诉我,十分感谢!
upd on 2024/10/14: 现在有了,感谢学弟让我见到了这个题!
例题:CF1038F Wrap Around
给定一个字符串,字符集为 { 0 , 1 } \{0, 1\} { 0 , 1 } 。求有多少长度为 n n n 的字符串 t t t ,满足以 t t t 为循环节的无限循环字符串包含 s s s 。
1 ≤ ∣ s ∣ ≤ n ≤ 40 1 \le |s| \le n \le 40 1 ≤ ∣ s ∣ ≤ n ≤ 4 0 ,但是我们把它加强到 4000 4000 4 0 0 0 。
TL: 2s, ML: 250MB.
加强到 4000 4000 4 0 0 0 做法还是挺多的,这里讲一个我的用到上面这个定理的做法。
考虑 s s s 出现的位置,如果在 t t t 中出现了,那可以设 d p i , j dp_{i, j} d p i , j 表示考虑前 i i i 个字符,s s s 匹配到 j j j 的方案数,然后用 KMP 自动机转移一下,在 j = ∣ s ∣ j = |s| j = ∣ s ∣ 时统计答案即可。
然后考虑所有 s s s 都经过头尾的方案,假设已经知道所有 s s s 覆盖的长度,剩余部分的方案数即为这样一个问题:求有多少长度为 l e n len l e n 的字符串 t t t ,满足 s + t + s s + t + s s + t + s 只在头尾能匹配到 s s s 。
做法和上面的 DP 类似,将初值设为 d p 0 , π ∣ s ∣ = 1 dp_{0, \pi_{|s|}} = 1 d p 0 , π ∣ s ∣ = 1 ,然后对于每个 i i i ,设答案为 s u m i sum_i s u m i ,则有:
s u m i = ∑ j ∉ P d p i , j sum_i = \sum\limits_{j \notin \mathcal{P}} dp_{i, j}
s u m i = j ∈ / P ∑ d p i , j
然后考虑 s s s 的方法,分类讨论:
如果只放了 1 1 1 个,则起点可以选在 s 2 s_2 s 2 到 s ∣ s ∣ s_{|s|} s ∣ s ∣ ,答案为 ( ∣ s ∣ − 1 ) s u m n − ∣ s ∣ (|s| - 1) sum_{n - |s|} ( ∣ s ∣ − 1 ) s u m n − ∣ s ∣ 。
如果放了 ≥ 3 \ge 3 ≥ 3 个,根据定理 4,相邻两个的位置差为最小周期,设为 p p p ,则放了 i i i 个的答案为 ( ∣ s ∣ − ( i − 1 ) p − 1 ) s u m n − ( ∣ s ∣ − ( i − 1 ) p ) (|s| - (i - 1)p - 1) sum_{n - (|s| - (i - 1)p)} ( ∣ s ∣ − ( i − 1 ) p − 1 ) s u m n − ( ∣ s ∣ − ( i − 1 ) p ) 。注意需要满足所有的 s s s 有交,即 ( i − 1 ) p < ∣ s ∣ (i - 1)p < |s| ( i − 1 ) p < ∣ s ∣ 。
如果放了 2 2 2 个,则枚举 Border 作为交,每次需要 check 中间能不能再匹配别的 s s s ,设交的长度为 i i i ,则答案为 ( i − 1 ) s u m n − ( 2 ∣ s ∣ − i ) (i - 1) sum_{n - (2|s| - i)} ( i − 1 ) s u m n − ( 2 ∣ s ∣ − i ) 。
注意需要保证 s s s 覆盖的长度 ≤ n \le n ≤ n 。
但是这样仍是有问题的,考虑 n = 5 , s = 101 n = 5, s = \verb|101| n = 5 , s = 101 ,发现当放了 2 2 2 个时第一个串和第二个串头尾重叠了,这是我们没有统计到的。因此对于放了 ≥ 2 \ge 2 ≥ 2 个的情况,需要判断一下第一次 ≥ n \ge n ≥ n 时能否叠在一起,如果可以则也要计入答案。
定理 5 :一个字符串 s s s 的所有长度不小于 ∣ s ∣ 2 \frac{|s|}{2} 2 ∣ s ∣ 的 Border 的长度构成一个等差数列。
证明:
设最长的 Border 的长度为 ∣ s ∣ − p |s| - p ∣ s ∣ − p ,另一个 Border 的长度为 ∣ s ∣ − q |s| - q ∣ s ∣ − q ,且有 p , q ≤ ∣ s ∣ 2 p, q \le \frac{|s|}{2} p , q ≤ 2 ∣ s ∣ 。
由弱周期引理可知 gcd ( p , q ) \gcd(p, q) g cd( p , q ) 也是 s s s 的周期,故 p ≤ gcd ( p , q ) p \le \gcd(p, q) p ≤ g cd( p , q ) ,因此 p ∣ q p \mid q p ∣ q 。
容易发现每次将最长的 Border 删去最后 p p p 个字符都会得到一个新的 Border,所以所有长度不小于 ∣ s ∣ 2 \frac{|s|}{2} 2 ∣ s ∣ 的 Border 构成了一个等差数列。
□ \square □
定理 6 :一个字符串的所有 Border 的长度排序后可以划分成 ⌈ log 2 ∣ s ∣ ⌉ \lceil\log_2|s|\rceil ⌈ log 2 ∣ s ∣ ⌉ 个连续段,使得每段都是一个等差数列。
首先可以将定理 5 扩展一下,把 ∣ s ∣ |s| ∣ s ∣ 也加进去,依然构成等差数列。
这样每次取出长度大于 ∣ s ∣ 2 \frac{|s|}{2} 2 ∣ s ∣ 的 Border,然后以剩下的最长 Border 作为 s s s ,递归划分即可。
□ \square □
基于定理 5 和定理 6,可以大大优化寻找 Border 和处理 Border 的复杂度。
例题:WC2016 论战捆竹竿
给定长度为 n n n 的字符串 s s s ,维护一个字符串 t t t ,初始为空,每次选则一个非负整数 a a a 使得 s u f ( t , ∣ t ∣ − a + 1 ) = p r e ( s , a ) \mathrm{suf}(t, |t| - a + 1) = \mathrm{pre}(s, a) s u f ( t , ∣ t ∣ − a + 1 ) = p r e ( s , a ) ,令 t ← t + s u f ( s , a + 1 ) t \gets t + \mathrm{suf}(s, a + 1) t ← t + s u f ( s , a + 1 ) ,问在 ∣ t ∣ ≤ w |t| \le w ∣ t ∣ ≤ w 的情况下 ∣ t ∣ |t| ∣ t ∣ 有多少种取值。
多组数据,T ≤ 5 , n ≤ 5 × 1 0 5 , w ≤ 1 0 18 T\le 5, n\le 5\times 10^5, w\le 10^{18} T ≤ 5 , n ≤ 5 × 1 0 5 , w ≤ 1 0 1 8 。
TL: 1s, ML: 120MB.
显然除了第一次外 a a a 可以是 s s s 的任意 Border 的长度,因此问题转化为求 ∑ a i x i \sum a_i x_i ∑ a i x i 在区间 [ 0 , w − ∣ s ∣ ] [0, w - |s|] [ 0 , w − ∣ s ∣ ] 中有多少种取值,其中 a i a_i a i 为 s s s 的周期。
然后考虑同余最短路,朴素做法是 O ( min P ( s ) × ∣ P ( s ) ∣ ) = O ( ∣ s ∣ 2 ) O(\min\mathcal{P}(s) \times |\mathcal{P}(s)|) = O(|s|^2) O ( min P ( s ) × ∣ P ( s ) ∣ ) = O ( ∣ s ∣ 2 ) 的。
考虑对每个等差数列分开来处理。对于一个等差数列 x , x + d , … , x + k d x, x + d, \ldots, x + kd x , x + d , … , x + k d ,在 m o d x {}\bmod x m o d x 意义下跑同余最短路,那就相当于每次可以 + 0 , + d , + 2 d , … , + k d +0, +d, +2d, \ldots, +kd + 0 , + d , + 2 d , … , + k d 。容易发现可以划分成 gcd ( x , d ) \gcd(x, d) g cd( x , d ) 个环,并且每个环互不影响,那就对每个环分开来做。然后对于一个环,能转移过来的就是一段区间,显然最小值不会被更新,因此从最小值开始断开变成链然后用单调队列维护就好了。
然后剩下来的问题就是合并,也就是要求模数改变后的结果。
设在模 p r e pre p r e 意义下的结果为 f f f ,模 n o w now n o w 意义下的结果为 g g g ,显然有 g i = min f j ≡ i ( m o d n o w ) f j g_i = \min_{f_j \equiv i \pmod{now}} f_j g i = min f j ≡ i ( m o d n o w ) f j 。
然后由于 f i f_i f i 的意义是 f i + k × p r e f_i + k\times pre f i + k × p r e 都能被走到,因此在 g g g 上 g i g_i g i 可以更新到 g i + k × p r e g_{i + k\times pre} g i + k × p r e ,因此需要再像上面一样跑一遍 DP。
时间复杂度为 O ( T n log n ) O(Tn\log n) O ( T n log n ) 。
回文 Border
定理 7 :回文串的回文前/后缀即为该串的 Border。
一般是根据这个性质可以把回文前缀转化成 Border 然后用定理 5 和定理 6优化。
定理 8 :若回文串 s s s 有周期 p p p ,则可以把 p r e ( s , p ) \mathrm{pre}(s, p) p r e ( s , p ) 划分成长度为 ∣ s ∣ m o d p |s| \bmod p ∣ s ∣ m o d p 的前缀和长度为 p − ∣ s ∣ m o d p p - |s| \bmod p p − ∣ s ∣ m o d p 的后缀,使得它们都是回文串。
画下图就明白了:
语言描述的严谨证明 :
用 r e v ( s ) \mathrm{rev}(s) r e v ( s ) 表示将 s s s 翻转后得到的串,则:p r e ( s , ∣ s ∣ m o d p ) = r e v ( s u f ( s , ∣ s ∣ − ∣ s ∣ m o d p + 1 ) ) \mathrm{pre}(s, |s|\bmod p) = \mathrm{rev}(\mathrm{suf}(s, |s| - |s| \bmod p + 1)) p r e ( s , ∣ s ∣ m o d p ) = r e v ( s u f ( s , ∣ s ∣ − ∣ s ∣ m o d p + 1 ) ) 。并且显然有 p r e ( s , ∣ s ∣ m o d p ) = s u f ( s , ∣ s ∣ − ∣ s ∣ m o d p + 1 ) \mathrm{pre}(s, |s|\bmod p) = \mathrm{suf}(s, |s| - |s| \bmod p + 1) p r e ( s , ∣ s ∣ m o d p ) = s u f ( s , ∣ s ∣ − ∣ s ∣ m o d p + 1 ) ,因此 p r e ( s , ∣ s ∣ m o d p ) \mathrm{pre}(s, |s|\bmod p) p r e ( s , ∣ s ∣ m o d p ) 是回文串。
考虑字符串 s ( ∣ s ∣ m o d p , s − ∣ s ∣ m o d p ] s_{(|s|\bmod p, s - |s|\bmod p]} s ( ∣ s ∣ m o d p , s − ∣ s ∣ m o d p ] ,若其长度超过 p p p ,显然 p p p 也是它的周期,同样方法可以证明 s ( ∣ s ∣ m o d p , p ] s_{(|s|\bmod p, p]} s ( ∣ s ∣ m o d p , p ] 是回文串。否则,显然为 s ( ∣ s ∣ m o d p , p ] s_{(|s|\bmod p, p]} s ( ∣ s ∣ m o d p , p ] 且是回文串。
□ \square □
定理 9 :若 t t t 是回文串 s s s 的最长 Border 且 ∣ t ∣ ≥ ∣ s ∣ 2 |t| \ge \frac{|s|}{2} ∣ t ∣ ≥ 2 ∣ s ∣ ,则 t t t 在 s s s 中只能匹配 2 2 2 次。
证明考虑反证,如果超过 2 2 2 次则可以推出更长的 Border。
这两个定理也没有找到什么应用……
Significant Suffixes
记 S s u f ( s ) \mathrm{Ssuf}(s) S s u f ( s ) 为在 s s s 后接任意一个串后可能成为最小后缀的串的集合。形式化地,t ∈ S s u f ( s ) ⟺ t\in \mathrm{Ssuf}(s) \iff t ∈ S s u f ( s ) ⟺ 存在一个字符串 u u u (可以为空)使得 t + u t + u t + u 是 s + u s + u s + u 的最小后缀。
定理 10 :对于任意一个字符串以及 u , v ∈ S s u f ( s ) , ∣ u ∣ < ∣ v ∣ u, v\in\mathrm{Ssuf}(s), |u| < |v| u , v ∈ S s u f ( s ) , ∣ u ∣ < ∣ v ∣ ,一定有 u u u 是 v v v 的 Border。
显然。
定理 11 :对于任意一个字符串 s s s 以及 u , v ∈ S s u f ( s ) , ∣ u ∣ < ∣ v ∣ u, v\in\mathrm{Ssuf}(s), |u| < |v| u , v ∈ S s u f ( s ) , ∣ u ∣ < ∣ v ∣ ,一定有 2 ∣ u ∣ ≤ ∣ v ∣ 2|u| \le |v| 2 ∣ u ∣ ≤ ∣ v ∣ 。
证明:
由定理 10 可得 u u u 是 v v v 的一个 Border。
假设 2 ∣ u ∣ > ∣ v ∣ 2|u| > |v| 2 ∣ u ∣ > ∣ v ∣ ,设与 u u u 对应的周期为 p p p ,记 t = p r e ( v , p ) t = \mathrm{pre}(v, p) t = p r e ( v , p ) ,则 u , v u, v u , v 可以表示为 u = t + w , v = t + t + w u = t + w, v = t + t + w u = t + w , v = t + t + w 。
若存在一个字符串 r r r 使得 u + r < v + r u + r < v + r u + r < v + r ,则 t + w + r < t + t + w + r t + w + r < t + t + w + r t + w + r < t + t + w + r ,即 w + r < t + w + r w + r < t + w + r w + r < t + w + r ,即 w + r < u + r w + r < u + r w + r < u + r ,因此永远无法找到一个字符串 r r r 使得 u + r < w + r u + r < w + r u + r < w + r 且 u + r < v + r u + r < v + r u + r < v + r ,因此 u ∉ S s u f ( s ) u \notin \mathrm{Ssuf}(s) u ∈ / S s u f ( s ) ,矛盾。
□ \square □
定理 12 :∣ S s u f ( s ) ∣ ≤ log 2 ∣ s ∣ |\mathrm{Ssuf}(s)| \le \log_2|s| ∣ S s u f ( s ) ∣ ≤ log 2 ∣ s ∣ 。
由定理 11 可得。
例题:ZJOI2017 字符串
维护一个动态字符串 s 1.. n s_{1..n} s 1 . . n ,字符串的字符集是所有 ∣ x ∣ ≤ 1 0 9 |x| \le 10^9 ∣ x ∣ ≤ 1 0 9 的整数。要求支持两个操作:
输入 l , r , d l, r, d l , r , d ,对于所有 l ≤ i ≤ r l \le i \le r l ≤ i ≤ r ,将 s i s_i s i 修改为 s i + d s_i + d s i + d ,注意 d d d 可能是负数。
输入 l , r l, r l , r ,输出子串 s l . . r s_{l..r} s l . . r 的字典序最小的后缀的起点位置。即,如果最小后缀是 s p . . r s_{p..r} s p . . r (l ≤ p ≤ r l \le p \le r l ≤ p ≤ r ),请输出 p p p 。
1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ q ≤ 3 × 1 0 4 , ∣ s i ∣ ≤ 1 0 8 , ∣ d ∣ ≤ 1 0 3 1 \le n \le 2\times 10^5, 1 \le q \le 3\times 10^4, |s_i| \le 10^8, |d| \le 10^3 1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ q ≤ 3 × 1 0 4 , ∣ s i ∣ ≤ 1 0 8 , ∣ d ∣ ≤ 1 0 3 。
TL: 3s, ML: 500MB.
用线段树维护,在线段树每个节点上维护这个区间的 S s u f \mathrm{Ssuf} S s u f ,合并左右儿子时,由于左右儿子长度几乎相等,因此左儿子只会保留一个。取出左儿子的所有 S s u f \mathrm{Ssuf} S s u f 进行比较,如果能比较出大小(指其中一个不是另一个的前缀)则扔掉大的那个,否则扔掉短的。
这样已经可以保证每个节点是 O ( log n ) O(\log n) O ( log n ) 个了,但并不是真正的 S s u f \mathrm{Ssuf} S s u f ,要把右儿子的集合也取出来与左儿子保留的那个进行比较,如果比出大小则扔掉大的那个。这样可以有效减小常数。
对于查询,由于只会找到线段树上 O ( log n ) O(\log n) O ( log n ) 个区间,可以把所有 O ( log 2 n ) O(\log^2 n) O ( log 2 n ) 个后缀都取出来进行比较。
后缀大小比较需要二分加哈希,由于有修改操作,哈希值也许要数据结构维护。如果用线段树,则复杂度为 O ( n log 3 n + m log 4 n ) O(n\log^3 n + m\log^4 n) O ( n log 3 n + m log 4 n ) ,用 O ( n ) O(\sqrt{n}) O ( n ) 修改 O ( 1 ) O(1) O ( 1 ) 查询的分块维护就可以变成 O ( n log 2 n + m log 3 n + m n ) O(n\log^2 n + m\log^3 n + m\sqrt{n}) O ( n log 2 n + m log 3 n + m n ) ,可以通过。
参考资料