
2026-03-20:统计有序数组中可被 K 整除的子数组数量。用go语言,给定一个非降序排列的整数数组 nums,以及一个正整数 k。
定义:如果数组中一段连续、非空的子数组,它所有元素的和能被 k 整除,那么这个子数组就是良好子数组。
要求:统计数组中所有不同的良好子数组的个数(只要子数组的位置 / 序列不同,就算不同的子数组)。
1 <= nums.length <= 100000。
1 <= nums[i] <= 1000000000。
nums 为非降序排列。
1 <= k <= 1000000000。
输入: nums = [1,2,3], k = 3。
输出: 3。
解释:
良好子数组为 [1, 2]、[3] 和 [1, 2, 3]。例如,[1, 2, 3] 是良好的,因为其元素和为 1 + 2 + 3 = 6,且 6 % k = 6 % 3 = 0。
题目来自力扣3729。
首先明确核心知识点:
sum[i] 表示数组前 i 个元素的累加和,子数组 [j+1, i] 的和 = sum[i] - sum[j];(sum[i] - sum[j]) % k == 0 等价于 sum[i] % k == sum[j] % k;cnt:{0:1}(初始化0的余数,对应前缀和为0的情况,是子数组判定的基础)sum:0lastStart:0ans:0i>0 不成立,不执行连续段统计逻辑;sum = 0 + 1 = 1;1 % 3 = 1,查询计数器cnt中余数1的数量为0;ans = 0 + 0 = 0;
当前状态:sum=1,ans=0,lastStart=0,cnt={0:1}i>0 成立,且x=2 != nums[0]=1,说明上一个连续段(仅元素1)结束;s=1,余数1%3=1,计数器cnt[1]变为1;lastStart = 1;sum = 1 + 2 = 3;3 % 3 = 0,查询计数器cnt[0]=1;ans = 0 + 1 = 1;
当前状态:sum=3,ans=1,lastStart=1,cnt={0:1, 1:1}i>0 成立,且x=3 != nums[1]=2,说明上一个连续段(仅元素2)结束;s=3,余数3%3=0,计数器cnt[0]变为2;lastStart = 2;sum = 3 + 3 = 6;6 % 3 = 0,查询计数器cnt[0]=2;ans = 1 + 2 = 3;
当前状态:sum=6,ans=3,lastStart=2,cnt={0:2, 1:1}最终答案为3,与题目输出一致。
0 ~ k-1,最多存储min(n, k)个键值对;.
package main
import (
"fmt"
)
func numGoodSubarrays(nums []int, k int) (ans int64) {
cnt := map[int]int{0: 1} // 为什么加个 0?见 560 题
sum := 0 // 前缀和
lastStart := 0 // 上一个连续相同段的起始下标
for i, x := range nums {
if i > 0 && x != nums[i-1] {
// 上一个连续相同段结束,可以把上一段对应的前缀和添加到 cnt
s := sum
forrange i - lastStart {
cnt[s%k]++
s -= nums[i-1]
}
lastStart = i
}
sum += x
ans += int64(cnt[sum%k])
}
return
}
func main() {
nums := []int{1, 2, 3}
k := 3
result := numGoodSubarrays(nums, k)
fmt.Println(result)
}

.
# -*-coding:utf-8-*-
from typing import List
def num_good_subarrays(nums: List[int], k: int) -> int:
"""
计算好子数组的个数
好子数组定义:子数组的和能被 k 整除
参数:
nums: 整数数组
k: 除数
返回:
好子数组的个数
"""
cnt = {0: 1} # 前缀和余数的计数器,初始化0出现1次
prefix_sum = 0 # 当前前缀和
last_start = 0 # 上一个连续相同段的起始下标
ans = 0
for i, x in enumerate(nums):
# 如果当前元素与前一个元素不同,说明上一个连续段结束
if i > 0 and x != nums[i-1]:
# 处理上一个连续相同段
s = prefix_sum
# 将上一段中所有可能的前缀和余数添加到计数器
for _ in range(i - last_start):
cnt[s % k] = cnt.get(s % k, 0) + 1
s -= nums[i-1]
last_start = i
prefix_sum += x
# 当前前缀和余数在计数器中出现的次数就是好子数组的个数
ans += cnt.get(prefix_sum % k, 0)
return ans
def main():
nums = [1, 2, 3]
k = 3
result = num_good_subarrays(nums, k)
print(result)
if __name__ == "__main__":
main()
.
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
long long numGoodSubarrays(vector<int>& nums, int k) {
unordered_map<int, int> cnt;
cnt[0] = 1; // 为什么加个 0?见 560 题
int sum = 0; // 前缀和
int lastStart = 0; // 上一个连续相同段的起始下标
long long ans = 0;
for (int i = 0; i < nums.size(); i++) {
int x = nums[i];
if (i > 0 && x != nums[i-1]) {
// 上一个连续相同段结束,可以把上一段对应的前缀和添加到 cnt
int s = sum;
for (int j = 0; j < i - lastStart; j++) {
cnt[s % k]++;
s -= nums[i-1];
}
lastStart = i;
}
sum += x;
ans += cnt[sum % k];
}
return ans;
}
int main() {
vector<int> nums = {1, 2, 3};
int k = 3;
long long result = numGoodSubarrays(nums, k);
cout << result << endl;
return0;
}

·
我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。 欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。
·