2011年就能出出这样充满科技感+脑力的题,佩服POI Orz
简记一下做法和证明,顺便复习 border 的一些性质。
注意真的是简记,有很多东西需要自己好好推一下……
约定:字符串 (s) 的下标从 (0) 开始,(s_{i,j}) 表示 (s) 的第 (i) 到第 (j) 个字符构成的子串。
串 (s) 有长度为 (l) 的周期等价于其有长度为 (|s|-l) 的 border。
设函数 (solve(s)) 表示字典序最小的、border 集合与 (s) 的 border 集合相同的 (01) 字符串。分为以下情况:
- 如果 (s) 中所有字符相同,返回 (|s|) 个 (0) 构成的字符串;
- 如果 (s) 周期集合为空,返回 (|s| - 1) 个 (0) 接上 (1) 个 (1) 构成的字符串;
- 如果 (s) 最短的周期长度 (len) 满足 (2len leq |s|),设 (t = s_{1,len}),则 (s) 可表示为 (ttt...tt') 的形式,其中 (t') 是 (t) 的一个前缀,可为空。
- 如果 (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| mod l)) 的元素一定构成集合 ({pl + (|s| mod l) | p in Z^+ , pl + (|s| mod l) leq |s|})。
Proof. 首先这些元素一定会包含在 border 集合内。考虑使用反证法证明不存在其他的元素。
设有不满足该形式的 border 长度 (l'),不妨设 (l' = xl+(|s| mod l)+y(y in [1,l-1]))。那么在 (s) 的长度为 ((x+1)l + (|s| mod l)) 的前缀中,(l') 是它的一个 border,即 (l-y) 是这个前缀的周期,而 (l) 也是其周期,且因为 (x geq 1) 有 ((x+1)l + (|s| mod 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 中所有利用 border 表示利用 border 导出的相等关系。充分利用相等关系是证明一些结论的关键。
对于 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|),利用 border 和 (s) 串首尾均是 (t) 串可以得到若干相等关系,进而得到 (t_{1,l-|t|-|a|}+a) 是 (t) 的一个周期,可表示为 (bab...bab) 形式,其中 (b) 是一个非全 (0) 字符串。此时设 (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|)。利用 border 和 (t) 串的循环串性质可以得到 (bab) 串中的某一段子串需要同时匹配 (a') 和 (a),这是不能做到的。
至此可以得出将 (a) 的最后一个 (0) 换成 (1) 得到的串一定是合法的,而它显然是最优的。
梳理一下 (solve(s)) 的流程:
- 如果 (s) 中所有字符相同,返回 (|s|) 个 (0) 构成的字符串;
- 如果 (s) 周期集合为空,返回 (|s| - 1) 个 (0) 接上 (1) 个 (1) 构成的字符串;
- 如果 (s) 最短的周期长度 (len) 满足 (2len leq |s|),将 (s) 表示为 (ttt...tt') 的形式,其中 (t') 是 (t) 的一个前缀,可为空,设 (qq' = solve(tt')),则返回 (q^{lfloor frac{|s|}{len} floor}q');
- 如果 (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))。