📜  门| GATE CS 2019 |第 57 题(1)

📅  最后修改于: 2023-12-03 15:12:37.036000             🧑  作者: Mango

题目介绍

本题为 2019 年 GATE 计算机科学考试第 57 题,题目难度较高,考察了程序员的算法能力和时间复杂度分析能力。

题目描述

有一个字符串 $S$,由 $0$ 和 $1$ 组成。你可以将 $S$ 分割成若干个子串,每个子串必须是连续的且长度为 $2^k$,其中 $k$ 是一个正整数。需要保证划分的子串中,每种子串出现的次数都不超过 $1$。求可以得到的最大子串个数。

时间限制

2 秒。

输入格式

单行输入一个长度不超过 $10^6$ 的 $01$ 串。

输出格式

输出一个整数,表示可以得到的最大子串个数。

样例输入
101101
样例输出
3
解题思路

首先,每个子串长度必须为 $2^k$,所以我们从 $k=0$ 开始递增,依次考虑 $2^k, 2^{k+1}, \dots, n$($n$ 为 $S$ 的长度)。对于当前的 $k$,我们用 $S$ 的前 $2^k$ 个字符构成一个子串,记为 $t$,然后在 $S$ 中查找第一个与 $t$ 相同的位置 $p$,此时 $S[p:p+2^k]$ 为一个符合要求的子串,我们将其标记为已选中,并用该子串更新 $t$,循环进行上述步骤,直到所有的子串均已选中或没有符合要求的子串可选。最终所选子串的个数即为答案。

应当注意的是,我们需要使用一些数据结构,如哈希表或后缀数组,来优化查找过程,以降低时间复杂度。

时间复杂度分析

假设 $S$ 的长度为 $n$。我们需要从 $k=0$ 开始遍历,直到 $k=\lfloor\log_2n\rfloor$。需要进行 $n/2^k$ 次查找操作,每次的时间复杂度为 $O(\log n)$(使用哈希表),总时间复杂度为 $O(n\log n)$。如果使用后缀数组,则总时间复杂度为 $O(n)$。

代码实现
def max_substrings(s):
    n = len(s)
    k = 0
    selected = set()
    while k <= n:
        t = s[:2**k]
        while t in selected:
            k += 1
            if k > n:
                return len(selected)
            t = s[:2**k]
        p = s.find(t, 2**k)
        while p != -1 and (p in selected or p+2**k-1 in selected):
            p = s.find(t, p+1)
        if p == -1:
            selected.add(s[:2**k])
            k += 1
        else:
            selected.update([s[p:p+2**k], s[:2**k]])
    return len(selected)

s = input().strip()
print(max_substrings(s))