AI刷题1.找单独的数|豆包MarsCode AI刷题

40 阅读2分钟

思路分析:

  1. 定义子序列和:

    • 给定一个长度为 ( n ) 的数组 ( a = [a_0, a_1, ..., a_{n-1}] ),我们可以定义一个子序列的和为从 ( a_i ) 到 ( a_j ) 的元素之和,记为: [ S(i, j) = a_i + a_{i+1} + \cdots + a_j ] 其中,( 0 \leq i \leq j < n )。
  2. 求解目标:

    • 我们要找到满足条件 ( S(i, j) % b == 0 ) 的子序列个数,即子序列和能够被 ( b ) 整除的个数。
  3. 优化方向:

    • 直接计算每个子序列的和并检查是否能被 ( b ) 整除的时间复杂度是 ( O(n^2) ),这对于较大的 ( n ) 是不合适的。

    • 我们可以利用前缀和的性质来优化计算:

      • 前缀和:定义 ( \text{prefix}[i] ) 为数组前 ( i ) 个元素的和,即: [ \text{prefix}[i] = a_0 + a_1 + \cdots + a_{i-1} ]
      • 子序列和 ( S(i, j) ) 可以通过前缀和表示为: [ S(i, j) = \text{prefix}[j+1] - \text{prefix}[i] ]
      • 如果 ( S(i, j) % b == 0 ),即 ( (\text{prefix}[j+1] - \text{prefix}[i]) % b == 0 ),则可以转化为: [ \text{prefix}[j+1] % b == \text{prefix}[i] % b ]
      • 这样,我们只需要记录前缀和对 ( b ) 取模后的值,并统计每个值出现的次数。
  4. 实现步骤:

    • 初始化一个哈希表来记录每个前缀和模 ( b ) 的出现次数。
    • 对于每个 ( j ),计算前缀和并对 ( b ) 取模,检查当前模值是否已经出现过,若出现过,则说明有子序列的和能够被 ( b ) 整除。

算法实现:

image.png

解释:

  1. 前缀和与模值的记录:

    • prefix_mod_b 哈希表记录了每个前缀和对 ( b ) 取模后的值出现的次数。初始时,prefix_mod_b[0] = 1 表示前缀和为 0 时,也就是从数组开头到当前下标的子序列和为 0。
  2. 遍历数组:

    • 遍历数组中的每个元素,累加到 prefix_sum 中,计算 prefix_sum % b
    • 如果某个模值已经出现过,说明从上一个出现该模值的前缀和位置到当前位置的子序列和能被 ( b ) 整除,因此我们将 prefix_mod_b[mod_value] 的值加到计数 count 中。
  3. 处理负数模值:

    • 由于 Python 中负数对模操作会产生负值,因此我们在计算模值时需要将负模值转为正数,即 mod_value += b

时间复杂度:

  • 遍历数组只需要一次 ( O(n) ),并且每次操作(更新前缀和,计算模值,查找哈希表)都是常数时间 ( O(1) )。
  • 因此,总的时间复杂度是 ( O(n) ),非常高效。

示例:

pythonCopy Code
# 示例输入
a = [3, 1, 4, 2, 5]
b = 3

# 计算结果
result = count_subarrays_divisible_by_b(a, b)
print(result)  # 输出符合条件的子序列个数

总结:

该算法通过前缀和与哈希表来优化计算,时间复杂度降到 ( O(n) ),大大提高了效率,能够快速解决问题。