📅  最后修改于: 2023-12-03 15:25:40.376000             🧑  作者: Mango
在给定的数组中,找到最长可能的子数组,使得该子数组的数字总和不能被K整除。本题需要返回符合要求的子数组数量。
我们可以通过前缀和来快速计算子数组的和,具体步骤如下:
preSum
,其中preSum[i]
表示原数组的前i个元素的和。[i, j]
,其和可以表示为preSum[j] - preSum[i-1]
。我们可以通过计算该子数组的余数来判断其是否满足条件。[i+1, j]
,其和为s - preSum[i]
。若(s - preSum[i]) % K
的余数为r,则之前出现过余数为r的位置为k,则当前子数组也满足要求,其长度为j - k
,可以加入答案中。首先考虑求解一个子问题:对于长度为k的子数组,如果它能被K整除,那么在该子数组前面加入任意长度的子数组后,它们的和一定也能被K整除。因此,我们可以使用一个状态dp[i][j],表示以第i个元素结尾的长度为j的子数组是否能被K整除。若第i个元素能被K整除,则dp[i][1] = true
。
然后我们考虑如何通过状态转移来求解答案:
dp[j][x] = true
的子数组(其中j为小于i的非负整数,x为小于j的正整数),如果在这些子数组后面加入第i个元素,其和仍然不能被K整除,那么它们对答案的贡献为1。同时,我们可以更新状态dp[i][1]
为true。dp[j][y] = true
的子数组(其中j为小于i的非负整数,y为大于等于1且小于j的正整数),如果在这些子数组后面加入第i个元素,其和仍然不能被K整除,那么它们对答案的贡献为j - y + 1。此时,我们可以更新状态dp[i][j-y+2]
为true。用代码表示即为:
if (nums[i] % K == 0)
ans = 1;
for (int j = i - 1; j >= 0; j--) {
for (int len = 1; len <= i - j; len++) {
if (nums[j+len-1] % K == 0) {
ans = max(ans, len);
break;
}
if ((preSum[j+len-1] - preSum[j] + nums[i]) % K != 0) {
ans = max(ans, len+1);
}
}
}
时间复杂度:O(n) 空间复杂度:O(K)
时间复杂度:O(n^2) 空间复杂度:O(n^2)
int subarraysDivByK(vector<int>& A, int K) {
int ans = 0, sum = 0;
unordered_map<int, int> cnt{{0, 1}};
for (int i = 0; i < A.size(); i++) {
sum = (sum + A[i]) % K;
if (sum < 0) sum += K;
ans += cnt[sum];
cnt[sum]++;
}
return ans;
}
int subarraysDivByK(vector<int>& nums, int K) {
int n = nums.size(), ans = 0;
vector<vector<bool>> dp(n, vector<bool>(n+1, false));
for (int i = 0; i < n; i++) {
if (nums[i] % K == 0) {
ans = 1;
dp[i][1] = true;
}
for (int j = i - 1; j >= 0; j--) {
for (int len = 1; len <= i - j; len++) {
if (nums[j+len-1] % K == 0) {
ans = max(ans, len);
break;
}
if (dp[j][len] && (preSum[j+len-1] - preSum[j] + nums[i]) % K != 0) {
ans = max(ans, len+1);
dp[i][len+1] = true;
}
}
}
}
return ans;
}