POI2011 Periodicity

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


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

约定:字符串 s 的下标从 0 开始,si,j 表示 s 的第 i 到第 j 个字符构成的子串。

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


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

  1. 如果 s 中所有字符相同,返回 |s|0 构成的字符串;
  2. 如果 s 周期集合为空,返回 |s|10 接上 11 构成的字符串;
  3. 如果 s 最短的周期长度 len 满足 2len|s|,设 t=s1,len,则 s 可表示为 ttt...tt 的形式,其中 tt 的一个前缀,可为空。
  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|s|,则 s 有长度为 gcd(p,q) 的周期。

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

  • 一个推论:设 s 最短周期长度为 l,且 2l|s|,则 s 的 border 集合中 l+(|s|modl) 的元素一定构成集合 {pl+(|s|modl)|pZ+,pl+(|s|modl)|s|}

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

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


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

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


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

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

  • l|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|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|t|+|a|,我们称前面插 a 的串为串 s,后面插 a 的串为串 t,此时由两个字符串相等,可以知道 s 里的 a 会跟 t 里的 baba...baba 的一个长度为 |a| 的子串匹配。首先它一定不会是 a,否则这个 border 长度就是原串长度。同时 a 的最后一个字符(也就是 1)不能出现在 aa 中。也就是说 a1 一定在 b 里面匹配,假设匹配了 bi。这里再进行一些讨论:
      • i<|a|,这意味着 a 的一段前缀 0 是跟 a 匹配的。注意到 sa 后面紧跟着的就是 babab...bab,根据相等,b 有一个长度为 L=|b|i 的 border,而 b 的周期是 000001,也就是说 b 的最后 i 个字符一定包含一个 1。而在 sa 前面的 i 个字符就是 b 的最后 i 个字符,它们在 t 里对应要相等的 i 个字符是 a 的前 i 个字符,因此必定有一个字符既是 0 又是 1,矛盾。
      • i=|a|,跟上面基本相同,但还需要考虑到的事情是 s 可能恰好以 a 开头。这个时候转换一下目标,sa 后面的那个 b 的长度为 |a| 的后缀在 t 里对应相等的是 a,所以也会有一个字符既是 1 又是 0,矛盾。
      • i>|a|,先不妨假设 sa 匹配的 b 不是 t 里的第一个,这样它前面有一个 a,剩余情况是类似的。设 sa 的前面 i|a| 个字符构成的字符串是 c,那么 cab 的一个周期,那么 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|10 接上 11 构成的字符串;
  3. 如果 s 最短的周期长度 len 满足 2len|s|,将 s 表示为 ttt...tt 的形式,其中 tt 的一个前缀,可为空,设 qq=solve(tt),则返回 q|s|lenq
  4. 如果 s 最短的周期长度 len 满足 2len>|s|,设 t=s1,len,q=solve(t)。检验 t+000...000+t 的最长 border 是否为 len,若否将最后一个 0 替换为 1,然后将该串返回。

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

posted @   cjoier_Itst  阅读(1063)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示