POI2011 Periodicity

Updated On 2022.02.01:情况 4 细节添加,也算是解决了第一次做这个题的时候一些口胡上的疑惑


2011年就能出出这样充满科技感+脑力的题,佩服POI Orz

约定:字符串 \(s\) 的下标从 \(0\) 开始,\(s_{i,j}\) 表示 \(s\) 的第 \(i\) 到第 \(j\) 个字符构成的子串。

\(s\) 有长度为 \(l\) 的周期等价于其有长度为 \(|s|-l\) 的 border。


设函数 \(solve(s)\) 表示字典序最小的、border 集合与 \(s\) 的 border 集合相同的 \(01\) 字符串。分为以下情况:

  1. 如果 \(s\) 中所有字符相同,返回 \(|s|\)\(0\) 构成的字符串;
  2. 如果 \(s\) 周期集合为空,返回 \(|s| - 1\)\(0\) 接上 \(1\)\(1\) 构成的字符串;
  3. 如果 \(s\) 最短的周期长度 \(len\) 满足 \(2len \leq |s|\),设 \(t = s_{1,len}\),则 \(s\) 可表示为 \(ttt...tt'\) 的形式,其中 \(t'\)\(t\) 的一个前缀,可为空。
  4. 如果 \(s\) 最短的周期长度 \(len\) 满足 \(2len > |s|\),设 border 为 \(t\),那么 \(s\) 就可以表示为 \(tat\) 的形式,其中 \(a\) 是一个任意字符串,不能为空。

1 和 2 情况的构造是显然的。


对于 3 情况,递归进入 \(tt'\) 求解。设 \(qq' = solve(tt')\),那么就把 \(q\) 重复若干次拼上 \(qq'\),即可得到答案。

在证明正确性前先来复习有关周期和 border 的几个 OI 界众所周知的简单定理。

  • Weak Periodicity Lemma:对于一个字符串 \(s\),若其有长度为 \(p\) 和长度为 \(q\) 的周期,且 \(p+q \leq |s|\),则 \(s\) 有长度为 \(\gcd(p,q)\) 的周期。

Proof. 设 \(p < q\),令 \(d = q - p\)。因为 \(p + q \leq |s|\),所以 \(\forall i \in [0,|s|-1] , i - p \geq 0\)\(i+q < |s|\) 至少有一个成立,故可以先向后跳 \(q\) 再向前跳 \(p\),或者先向前跳 \(p\) 再向后跳 \(q\),可得 \(s_{i+d} = s_i\)。那么 \(q-p\) 也是 \(s\) 的周期。根据辗转相除法,\(\gcd(p,q)\) 也是 \(s\) 的一个周期。QED

  • 一个推论:设 \(s\) 最短周期长度为 \(l\),且 \(2l \leq |s|\),则 \(s\) 的 border 集合中 \(\geq l + (|s| \bmod l)\) 的元素一定构成集合 \(\{pl + (|s| \bmod l) | p \in Z^+ , pl + (|s| \bmod l) \leq |s|\}\)

Proof. 首先这些元素一定会包含在 border 集合内。考虑使用反证法证明不存在其他的元素。

设有不满足该形式的 border 长度 \(l'\),不妨设 \(l' = xl+(|s| \bmod l)+y(y \in [1,l-1])\)。那么在 \(s\) 的长度为 \((x+1)l + (|s| \bmod l)\) 的前缀中,\(l'\) 是它的一个 border,即 \(l-y\) 是这个前缀的周期,而 \(l\) 也是其周期,且因为 \(x \geq 1\)\((x+1)l + (|s| \bmod l) \geq 2l-y\),根据 Periodicity Lemma\(\gcd(l,l-y)\) 是这个前缀的周期,同时 \(\gcd(l,l-y) \mid l\) 所以 \(\gcd(l,l-y)\) 是原串的一个周期,与 \(l\)\(s\) 的最短周期长度不符。QED


根据推论我们可以知道如果 \(q\) 串不是满周期串(即可划分为若干个部分满足各个部分的字符串完全相同),那么 \(\geq |qq'|\) 的所有 border 都可以正确构造,而构造 \(qq'\) 的时候将 \(< |qq'|\) 的所有 border 都已经正确构造了,所以这样就是对的。

证明 \(q\) 不是满周期串是简单的:如果 \(q\) 是满周期串,不妨设 \(q = p^c\),其中 \(p\) 是一个字符串。那么 \(|qq'|-|p|\)\(qq'\) 最长的 border。而如果在原串中有这样的一个 border 且 \(q\) 是周期,那么可以立即导出 \(p\) 是周期,那么 \(q\) 就不是最小周期。


对于 4 情况,先递归进入 \(s_{1,len}\) 求解。设 \(t = solve(s_{1,len})\),那么我们需要确定一个字典序最小的字符串 \(a\) 满足 \(s=tat\) 的最长 border 为 \(t\)

首先我们肯定希望 \(a\) 是全 \(0\) 的字符串。但是只有这一种选择显然不能覆盖所有的情况,比如 \(t = 0000\) 时这样会使得最长 border 变大导致构造不合法。考虑在什么情况下以全 \(0\) 串作为字符串 \(a\) 会导致不合法。不妨讨论有一个更长的 border \(l\)

  • \(l \leq |t| + |a|\)\(t\) 串在前面加若干个 \(0\) 等于在后面加若干个 \(0\),可导出 \(t\) 是全 \(0\) 串。这种情况下可以选择若干个 \(0\) 后面加上一个 \(1\) 形成的字符串,这样就是最优。
  • \(l > |t| + |a|\),假设 \(l\) 是最大的非平凡 border 长度,那么它有一个长度为 \(2|t|+|a|-l\) 的周期。又因为 \(|t|+|a|\) 也是它的周期,而 \(2|t|+|a|-l\) 是最短周期,所以 \(2|t|+|a|-l\)\(|t|+|a|\) 的约数,也就是说 \(t+a\) 是一个周期串。此时 \(2|t|+|a|-l \leq |a|\) 的情况 \(t\) 就是全零较为平凡,而在 \(2|t|+|a|-l > a\) 的情况下,我们总可以把 \(t\) 表示为 \(bab...bab\) 形式,其中 \(b\) 是一个非全 \(0\) 的字符串,且 \(|ba| = 2|t|+|a|-l\)。设 \(a'\) 为将 \(a\) 最后的 \(0\) 换成 \(1\) 得到的字符串,考虑把中间的 \(a\) 换成 \(a'\) 之后得到的字符串 \(s'\)。设它存在长度 \(>|t|\) 的 border ,不妨设长度为 \(l\)
    • \(l < |t| + |a'|\),在 \(t\) 前面加上 \(000...001\) 和在后面加上 \(000...000\) 得到相同的字符串,可以导出 \(t\) 中一个字符需要既等于 \(0\) 又等于 \(1\),不可能;
    • \(l \geq |t| + |a|\),我们称前面插 \(a'\) 的串为串 \(s\),后面插 \(a'\) 的串为串 \(t\),此时由两个字符串相等,可以知道 \(s\) 里的 \(a'\) 会跟 \(t\) 里的 \(baba...baba'\) 的一个长度为 \(|a|\) 的子串匹配。首先它一定不会是 \(a'\),否则这个 border 长度就是原串长度。同时 \(a'\) 的最后一个字符(也就是 1)不能出现在 \(a\)\(a'\) 中。也就是说 \(a'\)\(1\) 一定在 \(b\) 里面匹配,假设匹配了 \(b_i\)。这里再进行一些讨论:
      • \(i < |a|\),这意味着 \(a'\) 的一段前缀 \(0\) 是跟 \(a\) 匹配的。注意到 \(s\)\(a'\) 后面紧跟着的就是 \(babab...bab\),根据相等,\(b\) 有一个长度为 \(L = |b| - i\) 的 border,而 \(b\) 的周期是 \(000\cdots001\),也就是说 \(b\) 的最后 \(i\) 个字符一定包含一个 \(1\)。而在 \(s\)\(a'\) 前面的 \(i\) 个字符就是 \(b\) 的最后 \(i\) 个字符,它们在 \(t\) 里对应要相等的 \(i\) 个字符是 \(a\) 的前 \(i\) 个字符,因此必定有一个字符既是 0 又是 1,矛盾。
      • \(i = |a|\),跟上面基本相同,但还需要考虑到的事情是 \(s\) 可能恰好以 \(a'\) 开头。这个时候转换一下目标,\(s\)\(a'\) 后面的那个 \(b\) 的长度为 \(|a|\) 的后缀在 \(t\) 里对应相等的是 \(a\),所以也会有一个字符既是 \(1\) 又是 \(0\),矛盾。
      • \(i > |a|\),先不妨假设 \(s\)\(a'\) 匹配的 \(b\) 不是 \(t\) 里的第一个,这样它前面有一个 \(a\),剩余情况是类似的。设 \(s\)\(a'\) 的前面 \(i-|a|\) 个字符构成的字符串是 \(c\),那么 \(ca'\)\(b\) 的一个周期,那么 \(b\) 的长度为 \(i\) 的后缀应该是 \(ca'\) 的一个 rotate(就是不断把最后一个字符丢到第一个做若干次得到的字符串)。同时可以发现 \(a'\) 前面的 \(i\) 个字符对应到 \(t\) 上是 \(ac\),且它是 \(b\) 的一个后缀。也就是说 \(ac\) 必定等于 \(ca'\) 的一个 rotate,但是可以发现这两个字符串 1 的个数都不一样显然不可能相等,矛盾。

至此可以得出将 \(a\) 的最后一个 \(0\) 换成 \(1\) 得到的串一定是合法的,而它显然是最优的。


梳理一下 \(solve(s)\) 的流程:

  1. 如果 \(s\) 中所有字符相同,返回 \(|s|\)\(0\) 构成的字符串;
  2. 如果 \(s\) 周期集合为空,返回 \(|s| - 1\)\(0\) 接上 \(1\)\(1\) 构成的字符串;
  3. 如果 \(s\) 最短的周期长度 \(len\) 满足 \(2len \leq |s|\),将 \(s\) 表示为 \(ttt...tt'\) 的形式,其中 \(t'\)\(t\) 的一个前缀,可为空,设 \(qq' = solve(tt')\),则返回 \(q^{\lfloor \frac{|s|}{len} \rfloor}q'\)
  4. 如果 \(s\) 最短的周期长度 \(len\) 满足 \(2len > |s|\),设 \(t = s_{1,len} , q = solve(t)\)。检验 \(t+000...000+t\) 的最长 border 是否为 \(len\),若否将最后一个 \(0\) 替换为 \(1\),然后将该串返回。

至于如何 check 全 \(0\) 串是否合法,暴力 KMP 一遍即可。由 \(T(n) = T(\frac{n}{2}) + O(n)\) 可得复杂度为 \(O(n)\)

posted @ 2020-02-17 21:00  cjoier_Itst  阅读(1013)  评论(0编辑  收藏  举报