连续子串和的整数除问题 | 豆包MarsCode AI刷题
这题很巧妙地使用了前缀和与哈希表的性质,还要利用一点数学知识:a%b=c%b有(a-c)%b=0
摘要
本文介绍了如何在一个整数序列中找到所有和能被给定整数 整除的连续子序列。使用前缀和技巧和哈希表来高效解决这个问题。算法的核心在于使用前缀和的余数来快速判断子序列的和是否能被 整除,时间复杂度为 。代码实现包括Python和Go的版本。
问题描述
给定一个长度为 的正整数序列 ,以及一个正整数 ,目标是找到所有和能被 整除的连续子序列的数量。
示例
-
输入:
n = 3, b = 3, sequence = [1, 2, 3]
输出:3 -
输入:
n = 4, b = 5, sequence = [5, 10, 15, 20]
输出:10 -
输入:
n = 5, b = 2, sequence = [1, 2, 3, 4, 5]
输出:6
原理分析
1. 前缀和
在连续子序列和问题中,前缀和是一种常用技巧。前缀和数组 prefixSum[i] 表示从数组开头到第 i 个元素的累加和。
对于任意子序列 sequence[i...j],它的和可以表示为:
如果 prefixSum[j + 1] - prefixSum[i] 能被 整除,那么我们找到一个符合条件的子序列。
2. 前缀和的余数
为了更高效地判断子数组和是否能被 整除,可以使用前缀和的余数。
如果对于两个前缀和 prefixSum[i] 和 prefixSum[j],它们对 的余数相同,那么 prefixSum[j] - prefixSum[i] 一定能被 整除。
证明:
为了证明算法的正确性,我们需要证明在 的情况下,。
-
假设条件:已知 。
根据模运算的定义,存在整数 和 ,使得:
其中 是 和 对 取模的余数,因为 ,所以两者的余数 是相同的。
-
构造 :
计算 :
展开后得到:
将 提出来:
因此, 是 的整数倍。
-
取模运算:
根据模运算的性质,如果 是 的整数倍,那么:
因此,可以使用一个哈希表来记录前缀和的余数出现的次数,便于快速统计符合条件的子序列数量。
3. 算法步骤
- 初始化一个哈希表
remainderCount,用于记录前缀和余数出现的次数。 - 设置一个初始前缀和
currentSum = 0,并在哈希表中记录remainderCount[0] = 1(表示从起点到当前位置的前缀和余数为 0 的情况)。 - 遍历数组,依次更新
currentSum,计算currentSum % b,在哈希表中查询是否存在相同余数的前缀和。 - 将符合条件的子序列数累加到结果中。
代码实现
Python代码
from typing import List
def solution(n: int, b: int, sequence: List[int]) -> int:
"""
该函数接收整数 n、一个除数 b 和一个整数列表 sequence。
使用前缀和和哈希表来高效计算满足子数组和能被 b 整除的子数组数量。
"""
# 哈希表记录前缀和的余数出现次数
remainder_count = {0: 1} # 初始化,表示前缀和为 0 的情况出现一次
current_sum = 0 # 当前前缀和
count = 0 # 记录符合条件的子序列数目
# 遍历序列
for num in sequence:
current_sum += num
remainder = current_sum % b
# 检查当前余数是否已在哈希表中出现过
if remainder in remainder_count:
count += remainder_count[remainder]
# 更新余数的出现次数
remainder_count[remainder] = remainder_count.get(remainder, 0) + 1
return count
if __name__ == "__main__":
# 测试用例
print(solution(3, 3, [1, 2, 3])) # 输出:3
print(solution(4, 5, [5, 10, 15, 20])) # 输出:10
print(solution(5, 2, [1, 2, 3, 4, 5])) # 输出:6
Go语言代码
package main
import "fmt"
func solution(n int, b int, sequence []int) int {
// 哈希表记录前缀和的余数出现次数
remainderCount := make(map[int]int)
remainderCount[0] = 1 // 初始条件,表示前缀和为 0 的情况
currentSum := 0 // 当前前缀和
count := 0 // 记录符合条件的子序列数目
// 遍历序列
for _, num := range sequence {
currentSum += num
remainder := currentSum % b
// 检查当前余数是否已在哈希表中出现过
if freq, exists := remainderCount[remainder]; exists {
count += freq
}
// 更新余数的出现次数
remainderCount[remainder]++
}
return count
}
func main() {
// 测试用例
fmt.Println(solution(3, 3, []int{1, 2, 3})) // 输出:3
fmt.Println(solution(4, 5, []int{5, 10, 15, 20})) // 输出:10
fmt.Println(solution(5, 2, []int{1, 2, 3, 4, 5})) // 输出:6
}