1059.使数组和能被P整除

给你一个长度为 n 的整数数组 nums 和一个整数 p,请你选出一个 非空 的子数组使得该子数组元素和对 p 的余数是 0,但不能选出全部元素。

计算这个子数组的长度,如果不存在这样的子数组,返回 -1

一个数组的 子数组 定义为一个由数组中零个或者更多个连续元素组成的数组。

示例 1:

输入:nums = [3,1,4,2], p = 6 输出:1 解释:我们选出数列中的一个数字 4,使得它和剩下的数的和等于 6。

示例 2:

输入:nums = [6,3,5,2], p = 9 输出:2 解释:我们选出前两个数字,它们的和为 9,但它们不是整个数组的和。

示例 3:

输入:nums = [1,2,3], p = 3 输出:0 解释:这个数组本身就可以被 3 整除。

示例 4:

输入:nums = [1,2,3], p = 7 输出:-1 解释:没有任何子数组满足题意。

提示:

  • 1 <= n <= 10^5
  • 1 <= nums[i] <= 10^9
  • 1 <= p <= 10^9

解法

首先求出 nums 中所有元素的和 sum,如果 sum % p == 0,则整个数组就是一个符合要求的子数组,返回 0 即可。

如果 sum % p != 0,我们需要找到一个子数组,使得它的和对 p 取模等于 sum % p。我们可以用前缀和的方法来做。

具体来说,我们维护一个前缀和数组 prefix,其中 prefix[i] 表示 nums 中前 i 项的和。然后我们枚举右端点 i,左端点设为 j,判断子数组 nums[j..i] 的和是否等于 sum % p。如果等于,记录子数组长度和当前最小长度的较小值。如果不等于,继续枚举,直到找到符合要求的子数组或者枚举完所有子数组。

为了加速查找,我们可以使用哈希表来存储每个前缀和对 p 取模后的值以及对应的下标。在枚举右端点时,我们只需要查询哈希表中是否存在对应的前缀和即可。

代码实现

public int minSubarray(int[] nums, int p) {
    int len = nums.length;
    int mod = 0;
    for (int n : nums) {
        mod = (mod + n) % p;
    }
    if (mod == 0) {
        return 0;
    }
    int ans = Integer.MAX_VALUE;
    Map<Integer, Integer> map = new HashMap<>();
    map.put(0, -1);
    int sum = 0;
    for (int i = 0; i < len; i++) {
        sum = (sum + nums[i])%p;
        int gap = (sum - mod + p) % p;
        if (map.containsKey(gap)) {
            ans = Math.min(i - map.get(gap), ans);
        }
        map.put(sum, i);
    }
    return ans == len || ans == Integer.MAX_VALUE ? -1 : ans;
}

时间复杂度:O(n)

空间复杂度:O(n)

 

posted @ 2023-03-10 22:48  LYRiver  阅读(38)  评论(0编辑  收藏  举报