问题描述
给定一个长度为 的正整数序列 和一个正整数 ,要求找出有多少个连续子序列的和能够被 整除。
样例
样例1
- 输入:, , 序列 = [1, 2, 3]
- 输出:3
- 解释:符合条件的子序列有 [1, 2], [1, 2, 3], [3]
样例2
- 输入:, , 序列 = [5, 10, 15, 20]
- 输出:10
- 解释:符合条件的子序列有 [5], [10], [15], [20], [5, 10], [10, 15], [15, 20], [5, 10, 15], [10, 15, 20], [5, 10, 15, 20]
样例3
- 输入:, , 序列 = [1, 2, 3, 4, 5]
- 输出:6
- 解释:符合条件的子序列有 [1, 2], [3, 4], [1, 2, 3, 4], [1], [3], [5]
解题思路
前缀和与模运算
为了高效地解决这个问题,我们可以利用前缀和的概念和模运算的性质。具体来说,如果一个子序列的和能够被 整除,那么这个子序列的和模 的结果为 0。我们可以通过计算前缀和的模 来简化这个问题。
关键点
- 前缀和:定义 为从序列开始到第 个元素的和,即 。
- 模运算:如果一个子序列的和 能够被 整除,那么 ,这等价于 。
算法步骤
-
初始化:
- 创建一个数组
prefix_sum_mod用于存储每个前缀和模 的结果。 - 创建一个哈希表
mod_count用于记录每个模值出现的次数。 - 初始化变量
total_subsequences用于记录满足条件的子序列数量。
- 创建一个数组
-
计算前缀和并取模:
- 遍历序列,计算当前前缀和
current_sum,并将其模 的结果存储在prefix_sum_mod中。 - 更新哈希表
mod_count,记录每个模值出现的次数。
- 遍历序列,计算当前前缀和
-
计算满足条件的子序列数量:
- 遍历哈希表
mod_count,对于每个模值 出现 次,可以形成 个子序列。 - 特别地,如果某个前缀和的模值为 0,那么它本身也可以作为一个满足条件的子序列。
- 遍历哈希表
代码实现
def solution(n, b, sequence):
# 初始化前缀和数组和哈希表
prefix_sum_mod = [0] * n
mod_count = {0: 1} # 初始化模0的计数为1,因为前缀和为0的情况也算一个子序列
total_subsequences = 0
# 计算前缀和并取模
current_sum = 0
for i in range(n):
current_sum += sequence[i]
mod_value = current_sum % b
# 更新哈希表
if mod_value in mod_count:
mod_count[mod_value] += 1
else:
mod_count[mod_value] = 1
# 计算满足条件的子序列数量
for count in mod_count.values():
total_subsequences += (count * (count - 1)) // 2 # 组合的情况
return total_subsequences
if __name__ == "__main__":
# 测试样例
sequence1 = [1, 2, 3]
print(solution(3, 3, sequence1) == 3)
sequence2 = [5, 10, 15, 20]
print(solution(4, 5, sequence2) == 10)
sequence3 = [1, 2, 3, 4, 5]
print(solution(5, 2, sequence3) == 6)
代码解释
- 前缀和数组:
prefix_sum_mod存储每个前缀和模 的结果。 - 哈希表:
mod_count记录每个模值出现的次数,初始时模0的计数为1。 - 组合公式:对于每个模值 出现 次,可以形成 个子序列。
- 特殊情况:如果某个前缀和的模值为 0,那么它本身也是一个满足条件的子序列。
时间复杂度
- 时间复杂度:,其中 是序列的长度。我们只需要遍历一次序列来计算前缀和和更新哈希表。
- 空间复杂度:,哈希表的大小取决于模值的数量,最大为 。