1590. 使数组和能被 P 整除
大约 4 分钟
---
1590. 使数组和能被 P 整除
- 标签:数组、哈希表、前缀和
- 难度:中等
题目链接
题目大意
描述:给定一个正整数数组 和一个整数 。可以删除数组中一个子数组(可以为空)。
要求:返回使得剩余元素的和能被 整除所需删除的最小子数组长度。如果无法做到,返回 。
说明:
- 。
- 。
- 。
示例:
- 示例 1:
输入:nums = [3,1,4,2], p = 6
输出:1
解释:nums 中元素和为 10,不能被 p 整除。我们可以移除子数组 [4] ,剩余元素的和为 6 。- 示例 2:
输入:nums = [6,3,5,2], p = 9
输出:2
解释:我们无法移除任何一个元素使得和被 9 整除,最优方案是移除子数组 [5,2] ,剩余元素为 [6,3],和为 9 。
示例 3:
输入:nums = [1,2,3], p = 3
输出:0
解释:和恰好为 6 ,已经能被 3 整除了。所以我们不需要移除任何元素。
示例 4:
输入:nums = [1,2,3], p = 7
输出:-1
解释:没有任何方案使得移除子数组后剩余元素的和被 7 整除。
示例 5:
输入:nums = [1000000000,1000000000,1000000000], p = 3
输出:0解题思路
思路 1:前缀和 + 哈希表
1. 核心思想
设整个数组的和为 ,删除子数组 (下标从 到 )后,剩余和 = 。要求 ,即 。
设 。如果 ,不需要删除,返回 。
否则,需要在数组中寻找和为 (模 意义下)的最短子数组。
设前缀和数组 ,则子数组 的和模 为 。
条件 ,即 。
所以我们遍历 ,用哈希表记录每个 值最近出现的位置 。对于当前 ,查找前缀和为 的最右位置,子数组长度为 。
2. 具体步骤
第 1 步:计算 ,。如果 ,返回 。
第 2 步:初始化哈希表 ,记录每种前缀和模 值的最后出现位置。初始 。
第 3 步:遍历 ,维护当前前缀和 :
- 。
- 需要的前缀和 。
- 如果 在 中,子数组长度 = ,更新最小长度。
- 更新 。
第 4 步:返回最小长度。如果没找到,返回 。
3. 正确性证明
- 剩余和能被 整除 删除的子数组和模 等于整个数组和模 。
- 对于每个右端点 ,我们希望在左边找到最近的 使得 。
- 哈希表记录每种前缀和的最右位置,保证找到的子数组最短。
4. 举例说明
以 为例:
,。
- ,,, 不存在。。
- ,,,,长度 ,。。
- ,,,,长度 ,。。
- ,,,,长度 。
最短长度为 ,删除 (即 )。
验证:剩余 ,和 ,能被 整除。
思路 1:代码
class Solution:
def minSubarray(self, nums: List[int], p: int) -> int:
total = sum(nums)
target = total % p
if target == 0:
return 0
last_pos = {0: -1}
cur = 0
ans = len(nums)
for i, x in enumerate(nums):
cur = (cur + x) % p
need = (cur - target + p) % p
if need in last_pos:
ans = min(ans, i - last_pos[need])
last_pos[cur] = i
return ans if ans < len(nums) else -1思路 1:复杂度分析
- 时间复杂度:,一次遍历。
- 空间复杂度:,但实际存储的键最多 个。 很大时,哈希表大小 。