E. Check Transcription 解题思路
核心问题分析
题意非常浪漫:几十年前一台射电望远镜往外太空发了一段 01 串 ,最近收到了一段疑似外星人的回信 (全是小写字母)。科学家想验证 是不是 的“转写”:把 里所有的 0 替换成同一个字符串 ,把所有的 1 替换成同一个字符串 ,拼起来恰好等于 。要求 、 非空且 互不相同。问有多少对合法的 。
数据范围:,。设 里有 个 0、 个 1, 长 、 长 ,那么拼出来的总长必须满足
这一条等式就是整道题的命门——只要我们枚举其中一个长度,另一个就被锁死了。突破口在这。
1. 枚举一个长度,另一个白送
我们只枚举 的长度 (代码里的 one_len),那么 的长度立刻由那条等式解出来:
对应这两行:
int one_len = i, zero_len = (n_2 - i * one_n) / zero_n;
if (zero_len <= 0) break;
if ((n_2 - i * one_n) % zero_n != 0) continue;逻辑很干脆:分子算出来如果 ,说明 太大、再往后枚举只会更小,直接 break 收工;如果分子不能被 整除,那 不是整数,这个 作废,continue 跳过。只有整除且为正,才是一个长度合法的候选,值得我们去真正校验。
2. 让出现次数多的那个去当“被枚举者”——复杂度的灵魂一笔
你可能会问:枚举一个长度 次,每次都要扫一遍 校验 ,那不就是 起飞了吗?这里有一手非常漂亮的剪枝,藏在开头:
if (zero_n > one_n) {
swap(zero_n, one_n);
for (char& c : s_1) {
c = (c == '0' ? '1' : '0');
}
}它做的事是:保证 (如果 0 比 1 多,就把整个 的 0/1 对调,反正这只是给两类位置换个名字,答案数量丝毫不影响)。
为什么这么干?因为我们枚举的是出现次数更多的那一类(这里统一成 1)的长度 。出现次数多意味着 大,而 ,所以 能取的范围被压到 。又因为 ,于是有效的枚举次数大约是
每个有效候选要花 去扫一遍,乘起来差不多 级别——这就是经典的“调和级数 / 出现次数最多者长度有界”技巧。挑次数多的来枚举,能取的长度就少,整体被牢牢摁住,丝毫不慌。