leetcode-pp / 91alg-5-daily-check

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 24 】2021-10-03 - Delete Sublist to Make Sum Divisible By K #40

Open azl397985856 opened 3 years ago

azl397985856 commented 3 years ago

Delete Sublist to Make Sum Divisible By K

入选理由

暂无

题目地址

https://binarysearch.com/problems/Delete-Sublist-to-Make-Sum-Divisible-By-K

前置知识

Constraints

1 ≤ n ≤ 100,000 where n is the length of nums Example 1 Input nums = [1, 8, 6, 4, 5] k = 7 Output 2 Explanation We can remove the sublist [6, 4] to get [1, 8, 5] which sums to 14 and is divisible by 7.

yanglr commented 3 years ago

思路:

方法: 前缀和 + 哈希表

题意: 原数组中的任意数处于区间[1 ≤ n ≤ 100,000], 删除一个连续的子数组, 使得剩下的数的和是k的倍数, 存在就返回最短长度, 否则返回-1。

好像做过一个类似的题 - leetcode523。


基于前缀和和哈希表来做, 前缀和数组取名preSum, 哈希表取名dict。

用 rangeSum 表示要删除的子数组的和, rangeSum == preSum[j] - preSum[i]。

同余定理: (AllSum - rangeSum) % k == 0 <=> AllSum % k = rangeSum % k = (preSum2 - preSum1) % k <=> (AllSum + preSum1) % k = preSum2 % k

代码:

实现语言: C++

int floorMod(const int& a, const int& b)
{
    return (a % b + b) % b;
}   
int solve(vector<int>& nums, int k) {
    int allSum = 0;
    for (int& num : nums)
        allSum += num;

    allSum = floorMod(allSum, k);
    unordered_map<int, int> dict; // dict: 某个前缀和%k得到的余数->pos
    dict[0] = -1;

    int preSum = 0;
    int minLen = nums.size();
    for (int i = 0; i < nums.size(); i++) {
        preSum += nums[i];
        int mod = floorMod(preSum, k);
        dict[mod] = i;

        if (dict.count(floorMod(preSum - allSum, k)))
            minLen = min(minLen, i - dict[floorMod(preSum - allSum, k)]);
    }
    return minLen == nums.size() ? -1 : minLen;
}

复杂度分析

JiangyanLiNEU commented 3 years ago
class Solution:
    def solve(self, nums, k):
        n = len(nums)
        arr = nums
        mod_arr = [0] * n

    # Stores total sum of elements
        total_sum = 0

    # K has been added to each arr[i]
    # to handle -ve integers
        for i in range(n) :
            mod_arr[i] = (arr[i] + k) % k

        # Update the total sum
            total_sum += arr[i]

    # Remainder when total_sum
    # is divided by K
        target_remainder = total_sum % k

    # If given array is already
    # divisible by K
        if (target_remainder == 0):
            print("0")
            return

    # Stores curr_remainder and the
    # most recent index at which
    # curr_remainder has occured
        map1 = {}
        map1[0] = -1

        curr_remainder = 0

    # Stores required answer
        res = sys.maxsize

        for i in range(n):

        # Add current element to
        # curr_sum and take mod
            curr_remainder = (curr_remainder + arr[i] + k) % k

        # Update current remainder index
            map1[curr_remainder] = i

            mod = (curr_remainder - target_remainder + k) % k

        # If mod already exists in map
        # the subarray exists
            if (mod in map1.keys()):
                res = min(res, i - map1[mod])

    # If not possible
        if (res == sys.maxsize or res == n):
            res = -1

    # Print the result
        return (res)
mmboxmm commented 3 years ago

思路

前缀和变形

代码

fun minSublistLength(numbers: IntArray, k: Int): Int {
  val target = numbers.sum() % k
  if (target == 0) return 0

  var sum = 0
  val last = IntArray(k) { -2 }
  last[0] = -1

  var min = numbers.size
  for ((index, number) in numbers.withIndex()) {
    sum = (sum + number) % k
    val x = (sum - target + k) % k
    if (last[x] != -2) {
      min = minOf(min, index - last[x])
    }
    last[sum] = index
  }
  if (min == numbers.size) return -1
  return min
}

复杂度

falconruo commented 3 years ago

思路: 前缀和 + 哈希,利用同余定理

哈希表<前缀和的余数,整数下标>:key: 前i个整数和的余数(sum % p), value: 整数i

复杂度分析:

  1. 时间复杂度: O(n), 其中 n 为数组nums中的整数个数
  2. 空间复杂度: O(k), 哈希表里存放的余数个数

代码(C++):


class Solution {
public:
    int minSubarray(vector<int>& nums, int p) {
        int mode = 0;
        int sum = 0;

        for (auto& n : nums)
            sum += n;

        if (sum < p) return -1;

        mode = sum % p;

        if (mode == 0) return 0;

        unordered_map<int, int> mp = {{0, -1}}; 
        int res = nums.size();

        int tmpSum = 0;
        for (int i = 0; i < nums.size(); ++i) {
            tmpSum += nums[i];
            int tmpMode = tmpSum % p;
            int target = (tmpMode - mode + p) % p;
            if (mp.count(target))
                res = min(res, i - mp[target]);
            mp[tmpMode] = i;
        }

        return res == nums.size() ? -1 : res;
    }
};
Daniel-Zheng commented 3 years ago

思路

HashMap。

代码(C++)

int solve(vector<int>& nums, int k) {
    unordered_map<int, int> hashMapNums;
    int sum = 0;
    int prefix = 0;
    int res = nums.size();
    for (int i = 0; i < nums.size(); i++) sum += nums[i];
    sum = sum % k;
    hashMapNums[0] = -1;
    for (int i = 0; i < nums.size(); i++) {
        prefix += nums[i];
        hashMapNums[prefix % k] = i;
        if (hashMapNums.count((prefix - sum) % k)) res = min(res, i - hashMapNums[(prefix - sum) % k]);
    }
    return res == nums.size() ? -1 : res;
}

复杂度分析

pophy commented 3 years ago

思路

Java code

public int minSubarray(int[] nums, int k) {
    int res = nums.length;
    int p = 0;
    long[] presum = new long[nums.length + 1];//int会溢出。。。
    Map<Long, Integer> modIndexMap = new HashMap<>();
    modIndexMap.put(0l, -1);
    presum[0] = 0;
    for (int i = 0; i < nums.length; i++) {
        p = (p + nums[i]) % k;
        presum[i + 1] = presum[i] + nums[i];
    }
    if (p == 0) {
        return 0;
    }
    for (int j = 0; j < nums.length; j++) {
        long temp = presum[j + 1] % k;
        long curMod = (temp - p + k) % k;//防止出现负数
        if (modIndexMap.containsKey(curMod)) {
            int left = modIndexMap.get(curMod);
            res = Math.min(res, j - left);
        }
        modIndexMap.put(presum[j + 1] % k, j);
    }
    return res == nums.length ? -1 : res;
}

时间&空间

laofuWF commented 3 years ago
class Solution:
    def solve(self, nums, k):
        mod = 0
        total = 0
        for num in nums:
            total += num

        mod = total % k
        if mod == 0:
            return 0

        res = len(nums)
        total = 0
        mod_index_map = {0: -1}
        for j in range(len(nums)):
            total += nums[j]
            curr_mod = total % k
            target = (curr_mod - mod + k) % k
            if target in mod_index_map:
                res = min(res, j - mod_index_map[target])
            mod_index_map[curr_mod] = j

        if res == len(nums):
            return -1
        return res
xj-yan commented 3 years ago
class Solution {
    public int solve(int[] nums, int k) {
        int sum = 0;
        for (int n : nums) sum += n;
        int mod = sum % k;
        if (mod == 0) return 0;

        int minLength = nums.length + 1;
        sum = 0;
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);
        for (int i = 0; i < nums.length; i++){
            sum += nums[i];
            int curtMod = sum % k;
            int targetMod = (curtMod - mod + k) % k;
            if (map.containsKey(targetMod)){
                minLength = Math.min(minLength, i - map.get(targetMod));
            }
            map.put(curtMod, i);
        }
        return minLength == nums.length + 1 ? -1 : minLength;
    }
}

Time Complexity: O(n), Space Complexity: O(n)

leungogogo commented 3 years ago

Delete Sublist to Make Sum Divisible By K

Method. Prefix Sum + Map + Modular arithmetic

Main Idea

Obviously, to achieven a O(n) solution we need to use prefix sum and a hashmap to store previous results. But what information do we need to store?

(total - deleteSum) % k == 0 <--> total % k == deleteSum % k
(p[i] - p[j]) % k == total % k 
(p[i] - total) % k == p[j] % k

So we just need to store p[j] % k.

Code

int solve(vector& nums, int k) { int n = nums.size(), total = 0; vector preSum(n, 0); for (int i = 1; i <= n; ++i) { preSum[i] = preSum[i - 1] + nums[i - 1]; total += nums[i - 1]; }

if (total % k == 0) return 0;
// p[j] % k --> j
unordered_map<int, int> map;
int res = n;
for (int i = 0; i <= n; ++i) {
    int remainder = mod(preSum[i] - total, k);
    if (map.find(remainder) != map.end()) {
        res = min(i - map[remainder], res);
    }
    map[mod(preSum[i], k)] = i;
}
return res == n ? -1 : res;

}


### Complexity Analysis
Time: `O(n)`

Spapce: `O(n)`
ginnydyy commented 3 years ago

Problem

https://binarysearch.com/problems/Delete-Sublist-to-Make-Sum-Divisible-By-K

Notes

Solution

import java.util.*;

class Solution {
    public int solve(int[] nums, int k) {
        int result = nums.length;
        Map<Integer, Integer> map = new HashMap<>();

        int sum = 0;
        for(int n: nums){
            sum += n;
        }

        // sum[i, j] = presum[j + 1] - presum[i]
        // presum[i + 1] = presum[i] + nums[i]
        // presum[0] == 0, presum[1] = presum[0] + nums[0]
        int presum = 0;
        int mod = Math.floorMod(presum, k);
        map.put(mod, 0);

        for(int i = 0; i < nums.length; i++){
            presum += nums[i];
            mod = Math.floorMod(presum, k);
            map.put(mod, i + 1); // update the map always with the latest index

            int target = Math.floorMod(presum - sum, k);
            if(map.containsKey(target)){
                result = Math.min(result, i - map.get(target) + 1); // compare result and j - i + 1
            }
        } 

        return result == nums.length? -1 : result;
    }
}

Complexity

st2yang commented 3 years ago

思路

代码

复杂度

zol013 commented 3 years ago

思路: 用prefix sum 来计算subarray sum,用hashmap来记录subarray sum mod k对应 subarray right index,当prefix[i] - prefix[j] mod k == target mod k的时候说明 从i到j的subrray满足条件 -Python 3 Code:

class Solution:
    def minSubarray(self, nums: List[int], p: int) -> int:
        target = sum(nums) % p
        hashmap = {}
        hashmap[0] = -1
        prefix = 0 
        ans = len(nums)
        for i in range(len(nums)):
            prefix += nums[i]
            hashmap[prefix % p] = i
            if (prefix - target) % p in hashmap:
                ans = min(ans, i - hashmap[(prefix - target)%p])
        return ans if ans < len(nums) else -1 

Time Complexity: O(n) Space Complexity: O(n)

xieyj17 commented 3 years ago
class Solution:
    def solve(self, nums, k):
        residual = sum(nums) % k
        cumsum = 0
        hashmap = {0:-1}
        n = len(nums)
        res = n
        for i in range(n):
            cumsum += nums[i]
            mod = cumsum % k
            hashmap[mod] = i
            if (mod-residual)%k in hashmap:
                res = min(res, i-hashmap[(mod-residual)%k])
        if res == n:
            return -1
        else:
            return res

Space: O(N)

Time: O(N)

yingliucreates commented 3 years ago

link:

https://binarysearch.com/problems/Delete-Sublist-to-Make-Sum-Divisible-By-K

代码 Javascript

function deleteSubarray(arr, k) {
  // Stores the remainder of each
  // arr[i] when divided by K
  let mod_arr = new Array(arr.length);

  // Stores total sum of elements
  let total_sum = 0;

  // K has been added to each arr[i]
  // to handle negative integers
  for (let i = 0; i < arr.length; i++) {
    mod_arr[i] = (arr[i] + k) % k;

    // Update the total sum
    total_sum += arr[i];
  }

  // Remainder when total_sum
  // is divided by K
  let target_remainder = total_sum % k;

  // If given array is already
  // divisible by K
  if (target_remainder == 0) {
    return 0;
  }

  // Stores curr_remainder and the
  // most recent index at which
  // curr_remainder has occured
  let map = new Map();
  map.set(0, -1);

  let curr_remainder = 0;

  // Stores required answer
  let res = Number.MAX_SAFE_INTEGER;

  for (let i = 0; i < arr.length; i++) {
    // Add current element to
    // curr_sum and take mod
    curr_remainder = (curr_remainder + arr[i] + k) % k;

    // Update current remainder index
    map.set(curr_remainder, i);

    let mod = (curr_remainder - target_remainder + k) % k;

    // If mod already exists in map
    // the subarray exists
    if (map.has(mod)) res = Math.min(res, i - map.get(mod));
  }

  // If not possible
  if (res == Number.MAX_SAFE_INTEGER || res == arr.length) {
    res = -1;
  }

  return res;
}

复杂度分析

time and space O(n)

nonevsnull commented 3 years ago

思路

即lc 1590,用的lc题目条件写的。

TLE

AC

代码

//hashmap, presum, mod
class Solution {
    public int minSubarray(int[] nums, int p) {
        long sum = 0;

        for(int i: nums){
            sum += i;
        }
        if(sum < p) return -1;

        int mod = (int)(sum % p);
        if(mod == 0) return 0;

        HashMap<Integer, Integer> modMap = new HashMap<>();
        modMap.put(0, -1);
        long prefix = 0;
        int min = nums.length;
        for(int i = 0;i < nums.length;i++){
            prefix += nums[i];
            modMap.put((int)(prefix % p),i);
            if(modMap.containsKey((int)((prefix-mod) % p))){
                min = Math.min(min, i-modMap.get((int)((prefix-mod) % p)));
            }
        }
        return min == nums.length?-1:min;
    }
}

//presum brutal force, TLE
class Solution {
    public int minSubarray(int[] nums, int p) {
        long[] preSum = new long[nums.length];
        preSum[0] = (long)nums[0];
        long sum = (long)nums[0];
        for(int i = 1;i < nums.length;i++){
            preSum[i] = preSum[i-1] + (long)nums[i];

            sum += (long)nums[i];
        }
        long left = sum % p;
        if(left == 0) return 0;
        if(sum < p) return -1;

        int min = Integer.MAX_VALUE;

        for(int i = 0;i < preSum.length;i++){
            long l = i == 0?0: preSum[i-1];
            for(int j = i;j < preSum.length;j++){
                if((sum - preSum[j] + l) % p == 0 && sum != preSum[j]-l){
                    min = Math.min(min, j-i+1);
                }
            }
        }
        return min == Integer.MAX_VALUE?-1:min;
    }
}

复杂度

time: O(N) space: O(N)

//bf time: O(N^2) space: O(N)

tongxw commented 3 years ago

思路

总觉得这道题的关键在于能想到 (a-b) % k = c -> (a-b) % k = c % k,有点脑筋急转弯的意思。。。 (sum(0...n-1) - sum(i...j)) % k = 0 => sum(i...j) % k = sum(0...n-1) % k = mod => (preSum(j) - preSum(i-1)) % k = mod => (preSum(j) - preSum(i-1)) % k = mod % k 这步是关键 => (preSum(j) - preSum(i-1) - mod) % k = 0 => (preSum(j) - mod) % k = preSum(i-1) % k 或者 preSum(j) % k = (preSum(i-1) + mod) % k 先计算前缀和,然后用two sum的方法求解。 preSum(-1) % k = 0 % k = 0或者(preSum(-1) + mod) % k = mod % k = mod要先加到记忆哈希表里。

代码

class Solution {
    solve(nums, k) {
        const preSums = new Array(nums.length);
        preSums[0] = nums[0];
        for (let i=1; i<nums.length; i++) {
            preSums[i] = preSums[i-1] + nums[i];
        }

        let mod = preSums[nums.length - 1] % k;
        if (mod === 0) {
            return 0;
        }

        const memo = new Map();
        memo.set(0, -1); // 或memo.set(mod, -1)
        let ans = nums.length;
        for (let i=0; i<preSums.length; i++) {
            let curMod = preSums[i] % k;
            let target = (preSums[i] - mod) % k;
        // 或
        // let curMod = (preSums[i] + mod) % k;
            // let target = preSums[i] % k;
            if (memo.has(target)) {
                ans = Math.min(ans, i - memo.get(target));
            }
            memo.set(curMod, i);
        }

        return ans === nums.length ? -1 : ans;
    }
}

TC: O(n) SC: O(n)

yachtcoder commented 3 years ago

O(n) and O(n)

class Solution:
    def solve(self, nums, k):
        N = len(nums)
        psum = [0] + list(accumulate(nums))
        mod = psum[-1] % k
        ht = {0:-1}
        ret = N
        for i, p in enumerate(psum):
            km = (p-mod)%k
            ht[p%k] = i
            if km in ht:
                ret = min(i - ht[km], ret)
        return ret if ret != N else -1
wangzehan123 commented 3 years ago

class Solution {
    public int minSubarray(int[] A, int p) {
        int n = A.length, res = n, need = 0, cur = 0;
        for (int a : A)
            need = (need + a) % p;
        Map<Integer, Integer> last = new HashMap<>();
        last.put(0, -1);
        for (int i = 0; i < n; ++i) {
            cur = (cur + A[i]) % p;
            last.put(cur, i);
            int want = (cur - need + p) % p;
            res = Math.min(res, i - last.getOrDefault(want, -n));
        }
        return res < n ? res : -1;
    }
}

复杂度分析

令 n 为数组长度。

minuet-red commented 3 years ago

class Solution { public int minSubarray(int[] A, int p) { int n = A.length, res = n, need = 0, cur = 0; for (int a : A) need = (need + a) % p; Map<Integer, Integer> last = new HashMap<>(); last.put(0, -1); for (int i = 0; i < n; ++i) { cur = (cur + A[i]) % p; last.put(cur, i); int want = (cur - need + p) % p; res = Math.min(res, i - last.getOrDefault(want, -n)); } return res < n ? res : -1; } }

q815101630 commented 3 years ago

Thought:

使用前缀和+ 哈希表。做了好几道前缀和的题,重点在于找到当前preSum和 与preSum[i]的关系。所谓前缀和,不一定必须是和,也可以是某种函数其函数对应的值会随着输入list增加而增加。因为preSum[i] (A)就是我们存在表里的键,一旦能够和当前preSum链接,那么就能找到答案。 设A+B = list, A和B为contiguous sublist 这道题的关系为: (preSum-target) %k = A%k

class Solution:
    def solve(self, nums, k):
        target = sum(nums) % k
        cnt = {0:-1}
        preSum = 0
        ans = len(nums)
        for i in range(len(nums)):
            preSum += nums[i]

            mod = preSum%k

            cnt[mod] = i
            if (preSum-target) %k in cnt:
                ans = min(ans, i-cnt[(preSum-target)%k])

        if ans < len(nums):
            return ans
        else:
            return -1

时空都为O(n)

JK1452470209 commented 3 years ago

思路

太难了..

代码

    class Solution {

        public int solve(int[] nums, int k) {

            int tar = 0;

            for (int n : nums)
                tar += n;

            tar = Math.floorMod(tar, k);

            Map<Integer, Integer> map = new HashMap<>();
            map.put(0, -1);

            int prefix = 0, res = nums.length;

            for (int i = 0; i < nums.length; i++) {

                prefix += nums[i];
                int mod = Math.floorMod(prefix, k);
                map.put(mod, i);

                if (map.containsKey(Math.floorMod(prefix - tar, k)))
                    res = Math.min(res, i - map.get(Math.floorMod(prefix - tar, k)));
            }

            return res == nums.length ? -1 : res;
        }
    }

复杂度

时间复杂度:O(N)

空间复杂度:O(min(N,k))

HouHao1998 commented 3 years ago

思想

前缀和,哈希表

代码 Java

 public int solve(int[] nums, int k) {
            int res = nums.length;
            int p = 0;
            long[] presum = new long[nums.length + 1];
            Map<Long, Integer> modIndexMap = new HashMap<>();
            modIndexMap.put(0L, -1);
            presum[0] = 0;
            for (int i = 0; i < nums.length; i++) {
                p = (p + nums[i]) % k;
                presum[i + 1] = presum[i] + nums[i];
            }
            if (p == 0) {
                return 0;
            }
            for (int j = 0; j < nums.length; j++) {
                long temp = presum[j + 1] % k;
                long curMod = (temp - p + k) % k;
                if (modIndexMap.containsKey(curMod)) {
                    int left = modIndexMap.get(curMod);
                    res = Math.min(res, j - left);
                }
                modIndexMap.put(presum[j + 1] % k, j);
            }
            return res == nums.length ? -1 : res;
        }
    }

复杂度

时间复杂度:O(N) 空间复杂度:O(min(N,K))

BlueRui commented 3 years ago

Problem 1590. Make Sum Divisible by P

Algorithm

Complexity

Code

Language: Java

public int minSubarray(int[] nums, int p) {
    int target = 0;
    for (int num : nums) {
        // Use modulo to sum to avoid overflow
        target = (target + num) % p;
    }

    Map<Integer, Integer> map = new HashMap<>();
    int prefixSumMod = 0;
    int result = nums.length;

    map.put(0, -1);
    for (int i = 0; i < nums.length; i++) {
        // Use modulo to sum to avoid overflow
        prefixSumMod = (prefixSumMod + nums[i]) % p;
        map.put(prefixSumMod, i);

        int targetKey = Math.floorMod(prefixSumMod - target, p);
        if (map.containsKey(targetKey)) {
            result = Math.min(result, i - map.get(targetKey));
        }
    }
    return result == nums.length ? -1 : result;
}
kennyxcao commented 3 years ago

Delete Sublist to Make Sum Divisible By K

Problem Source

Intuition

Code

class Solution {
  solve(nums, k) {
    const n = nums.length;
    const remainder = nums.reduce((rem, num) => (rem + num) % k, 0);
    if (remainder === 0) return 0; // entire nums already divisible by k
    let preSum = 0;
    let minLen = n;
    const last = new Map(); // prefixSum % p -> last seen index
    last.set(preSum, -1);
    for (let i = 0; i < n; ++i) {
      preSum = (preSum + nums[i]) % k;
      const key = (preSum - remainder + k) % k; // make sure key is within [0:k-1]
      if (last.has(key)) {
        minLen = Math.min(minLen, i - last.get(key));
      }
      last.set(preSum, i);
    }
    return minLen === n ? -1 : minLen;
  }
}

Complexity Analysis

biscuit279 commented 3 years ago

思路:前缀和+同余定理

class Solution(object):
    def minSubarray(self, nums, p):
        """
        :type nums: List[int]
        :type p: int
        :rtype: int
        """
        N =len(nums)
        pre_sum = [0]
        for num in nums:
            pre_sum.append(pre_sum[-1] + num)
        mod = pre_sum[-1] % p
        mod_map = {0:-1}
        ans = N
        for i,psum in enumerate(pre_sum):
            pre_mod = (psum-mod) % p
            mod_map[psum%p] = i
            if pre_mod in mod_map:
                ans = min(i - mod_map[pre_mod], ans)
        if ans != N:
            return ans
        else:
            return -1

时间复杂度:O(N) 空间复杂度:O(N)

okbug commented 3 years ago

代码

语言:C++

int solve(vector<int>& nums, int k) {
    int n = nums.size();
    vector<int> arr;
    int total = 0;
    for (int i = 0; i < n; i++) {
        arr.push_back((nums[i] + k) % k);
        total += nums[i];
    }

    int target = total % k;
    if (target == 0) return 0;
    unordered_map<int, int> map;
    map[0] = -1;

    int cur = 0;
    int res = n;
    for (int i = 0; i < n; i++) {
        cur = (cur + nums[i] + k) % k;
        map[cur] = i;

        int mod = (cur - target + k) % k;
        if (map.count(mod)) 
            res = min(res, i - map[mod]);
    }
    if (res == n) return -1;
    return res;
}
carterrr commented 3 years ago

public class 删除最短长度使得数组中余下数字能被k整除{ public int solve(int[] nums, int k) { int targetMod = 0,prefixSum = 0, res = nums.length; for(int num : nums) { targetMod += num; } targetMod %= k; Map<Integer, Integer> map = new HashMap<>(); map.put(0, -1); // mod余0 从0开始 加上第0位 放 -1 for(int i = 0; i < nums.length; i ++) { prefixSum += num; int mod = Math.floorMod(prefixSum - targetMod, k); if(map.containsKey(mod)) { res = Math.min(i - map.get(mod), res); }

        // i - 1用于在减去求长度的时候加上第i位
        map.put(Math.floorMod(prefixSum, k), i - 1);
    }

    return res == nums.length ? -1 : res;

}

}

septasset commented 3 years ago

思考

看题解复现

关键点

  1. 前缀和计算连续数组的和
  2. 目标: (total - sum_range) % k == 0
  3. 负数的取余
  4. dist_min初始化

代码(Python)

class Solution:
    def solve(self, nums, k):
        pres = []
        pre = 0
        for i in range(len(nums)):
            pre = (pre + nums[i]) % k
            pres.append(pre)

        total = pres[-1] % k
        occ_last = dict()
        dist_min = len(nums)
        occ_last[0] = -1
        for j in range(len(nums)):
            occ_last[pres[j]] = j
            target = (pres[j] - total) % k
            if target in occ_last:
                dist_min = min(dist_min, j-occ_last[target])
        return dist_min if dist_min != len(nums) else -1

复杂度分析

shamworld commented 3 years ago

思路

抄下

import java.util.*;

class Solution {

    public int solve(int[] nums, int k) {

        int tar = 0;

        for (int n : nums)
            tar += n;

        tar = Math.floorMod(tar, k);

        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);

        int prefix = 0, res = nums.length;

        for (int i = 0; i < nums.length; i++) {

            prefix += nums[i];
            int mod = Math.floorMod(prefix, k);
            map.put(mod, i);

            if (map.containsKey(Math.floorMod(prefix - tar, k)))
                res = Math.min(res, i - map.get(Math.floorMod(prefix - tar, k)));
        }

        return res == nums.length ? -1 : res;
    }
}
biancaone commented 3 years ago

思路 🏷️

非常难的一道题,看了很多小伙伴的答案,选了一个最能理解的抄的。谢谢各位的思路。


代码 📜

class Solution:
    def solve(self, nums, k):
        N = len(nums)
        presum = [0] + list(accumulate(nums))
        mod = presum[-1] % k
        ht = {0:-1}
        s = N
        for i, p in enumerate(presum):
            km = (p-mod)%k
            ht[p%k] = i
            if km in ht:
            s = min(i - ht[km], s)
        return s if s != N else -1
yiwchen commented 3 years ago

思路: prefix sum with modular

def solve(self, nums, k):
    ans = float("inf")
    s = sum(nums)%p
    d = {0:-1}
    cum = 0
    if s == 0:
        return 0
    for i in range(len(nums)):
        cum+=nums[i]
        r = cum%p
        if (r-s)%p in d: 
            ans = min(ans, i-d[(r-s)%p])
        d[r] = i
    return ans if ans<len(nums) else -1

TC:O(n) SC:O(n))

MoncozGC commented 3 years ago
public int minSubarray(int[] nums, int p) {
    int target = 0;
    for (int num : nums) {
        // Use modulo to sum to avoid overflow
        target = (target + num) % p;
    }

    Map<Integer, Integer> map = new HashMap<>();
    int prefixSumMod = 0;
    int result = nums.length;

    map.put(0, -1);
    for (int i = 0; i < nums.length; i++) {
        // Use modulo to sum to avoid overflow
        prefixSumMod = (prefixSumMod + nums[i]) % p;
        map.put(prefixSumMod, i);

        int targetKey = Math.floorMod(prefixSumMod - target, p);
        if (map.containsKey(targetKey)) {
            result = Math.min(result, i - map.get(targetKey));
        }
    }
    return result == nums.length ? -1 : result;
}
yibenxiao commented 3 years ago

【Day 24】Delete Sublist to Make Sum Divisible By K

思路

暴力解法

代码

class Solution:
    def solve(self, nums, k):
        sums = sum(nums)
        ans = -1
        len_nums = len(nums)
        for i in range(len_nums):
            temp = []
            j = 0
            count = 0
            tmp_sum = 0
            while i + j < len_nums:
                tmp_sum += nums[i + j]
                count += 1
                if (sums - tmp_sum) % k == 0:
                    if count < ans or ans == -1:
                        ans = count
                    break

        return ans

复杂度

时间复杂度:O(N^2)

空间复杂度:O(1)

vincentLW commented 3 years ago
fun minSublistLength(numbers: IntArray, k: Int): Int {
  val target = numbers.sum() % k
  if (target == 0) return 0

  var sum = 0
  val last = IntArray(k) { -2 }
  last[0] = -1

  var min = numbers.size
  for ((index, number) in numbers.withIndex()) {
    sum = (sum + number) % k
    val x = (sum - target + k) % k
    if (last[x] != -2) {
      min = minOf(min, index - last[x])
    }
    last[sum] = index
  }
  if (min == numbers.size) return -1
  return min
}

TC:O(n) SC:O(n))

ivalkshfoeif commented 3 years ago
import java.util.*;

class Solution {
    public int solve(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);
        int presum = 0;
        int sum = 0;
        for (int num : nums){
            sum += num;
        }

        int target = Math.floorMod(sum, k);
        int minL = nums.length;
        for (int i = 0; i < nums.length; i++){
            presum += nums[i];
            int mod = Math.floorMod(presum, k);
            map.put(mod, i);
            if (map.containsKey(Math.floorMod(presum - target, k))){
                minL = Math.min(minL, i - map.get(Math.floorMod(presum - target, k)) );
            }

        }

        if (minL == nums.length){
            return -1;
        }
        return minL;
    }
}

corner case 意外的挺多的, 学到Math.floorMod

chenming-cao commented 3 years ago

解题思路

前缀和+同余定理+哈希表。此题要求连续的子数组,适合使用前缀和。先求出数组总和,然后模k,如果余数为0,则删除子数组最小长度为0。如果余数不为0,根据同余定理,则需找到一个模k的余数和数组总和余数相等的子数组,两数组之差模k余数也为0。为了快速找到符合要求的前缀和,我们用哈希表储存余数和对应的前缀和。

代码

class Solution {
    public int solve(int[] nums, int k) {
        int len = nums.length;
        int tar = 0;
        for (int num: nums) {
            tar = (tar + num) % k; // use modulo to avoid overflow
        }

        int prefix = 0;
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1); // initial condition in the map: no element, mod = 0, index set as -1
        int res = len;

        for (int i = 0; i < len; i++) {
            prefix = (prefix + nums[i]) % k; // avoid overflow
            map.put(prefix, i);

            int mod = Math.floorMod(prefix - tar, k);
            if (map.containsKey(mod)) {
                res = Math.min(res, i - map.get(mod));
            }
            map.put(Math.floorMod(prefix, k), i);
        }
        return res != len ? res : -1;                
    }
}

复杂度分析

Moin-Jer commented 3 years ago

思路


根据数组的前缀和,计算前缀和的余数,并使用哈希表保存余数对应的数组下标

代码


import java.util.*;

class Solution {
    public int solve(int[] nums, int k) {
        int target = 0;
        for (int n : nums) {
            target += n;
        }
        target %= k;
        int prefix = 0, ans = nums.length;
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);
        for (int i = 0; i < nums.length; ++i) {
            prefix += nums[i];
            map.put(prefix % k, i);
            if (map.containsKey((prefix - target) % k)) {
                ans = Math.min(ans, i - map.get((prefix - target) % k));
            }
        }
        return ans == nums.length ? -1 : ans;
    }
}

复杂度分析


15691894985 commented 3 years ago

【day4】Delete Sublist to Make Sum Divisible By K

https://binarysearch.com/problems/Delete-Sublist-to-Make-Sum-Divisible-By-K

方法:同余定理+前缀和优化

目标:移除一段最短连续子数组,使得剩下数字和为K的整数倍

前缀和:数组A[i:j]的和是pre[j]-pre[i-1],其中pre为A的前缀和

同余定理:两个模k余数相同的数字相减得到的值可以被k整除

代码:

    class Solution(object):
        def minSubarray(self, nums, k):
            N = len(nums)
            pre = [0] #前缀和
            for num in nums:
                pre.append(pre[-1]+num)
            mod = pre[-1]  % k #total %k=target
            has_map = {0:-1}
            ans  = N
            for i, psum in enumerate(pre):
                pre_mod = (psum-mod) % k #(pre[j]-target)%k = pre[i]%k
                has_map[psum%k] = i
                if pre_mod in has_map:
                    ans = min(i-has_map[pre_mod],ans)
            if ans !=N:
                return ans
            else:
                return  -1

复杂度:

asterqian commented 3 years ago

思路

Leetcode 1590 https://leetcode-cn.com/problems/make-sum-divisible-by-p/
【C++注意溢出】利用同余定理和前缀和,totalSum % k == deleteSum % k,又已知deleteSum = pres[j] - pres[i - 1],易得(pres[j] - target(== totalSum % k)) % k == pres[i - 1] % k。由此,我们将前缀和%k加入hashmap,同时检查其减去target再%k是否已经存在在表中,如果已经存在,则将答案更新为其已存在的index与现在index之差,即为需要删除的subarray的长度,遍历的过程中如果遇到更小的delete subarray长度,便需要更新res。

代码

class Solution {
public:
    int minSubarray(vector<int>& nums, int p) {
        long long target = 0;
        for (long long n : nums) target += n;
        target %= p; // total % k
        if (target == 0) return 0; // already divisible
        unordered_map<int, int> map;
        map[0] = -1; // case of subarray starting w/ index 0
        long long prefix = 0;
        long long res = nums.size();
        for (long long i = 0; i < nums.size(); ++i) {
            prefix += nums[i];
            map[prefix % p] = i;
            if (map.find((prefix - target) % p) != map.end()) {
                // later one is the length of the subarr that needs to be deleted
                res = min(res, i - map[(prefix - target) % p]);
            }
        }
        return res == nums.size() ? -1 : res;
    }
};

复杂度分析

时间复杂度: O(N)
空间复杂度: O(min(N, p)) 因为都需要mod p
Jding0 commented 3 years ago

思路

copy from others

code

class Solution:
    def solve(self, nums, k):
        target = sum(nums) % k
        hashmap = {}
        hashmap[0] = -1
        prefix = 0 
        ans = len(nums)
        for i in range(len(nums)):
            prefix += nums[i]
            hashmap[prefix % k] = i
            if (prefix - target) % k in hashmap:
                ans = min(ans, i - hashmap[(prefix - target)%k])
        return ans if ans < len(nums) else -1 
kidexp commented 3 years ago

thoughts

先计算整个的和 mod k的值 然后计算每个prefix sum mod k的值 如果pref_sum mod k和 整体和mod k的差在之前出现就更新最小的length

code

class Solution:
    def solve(self, nums, k):
        sum_ = sum(nums) % k
        if sum_ == 0:
            return 0
        sum_map = {0: -1}
        prefix_sum = 0
        min_length = None
        for i, num in enumerate(nums):
            prefix_sum += num
            prefix_sum_mod = prefix_sum % k

            mod_diff = prefix_sum_mod - sum_
            if mod_diff < 0:
                mod_diff += k

            if mod_diff in sum_map:
                min_length = (
                    i - sum_map[mod_diff]
                    if min_length is None
                    else min(min_length, i - sum_map[mod_diff])
                )

            sum_map[prefix_sum_mod] = i
        return -1 if min_length is None or min_length == len(nums) else min_length

complexity

time O(n)

space O(min(n, k))

carsonlin9996 commented 3 years ago

pre[j] - target % k = pre[i-1]%k


class Solution {
    public int solve(int[] nums, int k) {

        Map<Integer, Integer> map = new HashMap<>();

        map.put(0, -1);

        int preSum = 0;

        int sum;

        for (int num : nums) {
            sum += num;
        }

        int target = Math.floorMod(target, k);
        int res = nums.length;

        for (int i = 0; i < nums.length; i++) {
            preSum += nums[i];

            int mod = Math.floorMod(preSum, k);

            map.put(mod, i);

            if (map.containsKey(Math.floorMod(preSum - target, k))) {
                res = Math.min(res, i - map.get(Math.floorMod(preSum - target, k)));
            }
        }

        return res == nums.length ? -1 : res;
    }
}
a244629128 commented 3 years ago


//(total - sum of some subarray) % p will be 0.
// In other words:(total - (sum[i] - sum[j]))%p = 0 can be expressed as sum[j]%p = sum[i]%p -total %p. I use the mod function to help evaluate negative values correctly.
var minSubarray = function(nums, p) {
    var map = new Map();
    map.set(0,-1);
    var res = nums.length;
    var total = 0;
    var currSum = 0;
    for(let i =0; i<nums.length;i++){
        total+= nums[i];
    }
    //total %p
    total = total%p;
    for(let i = 0; i<nums.length;i++){
        //sum[i]%p current sum
        currSum = (nums[i]+currSum)%p;
        map.set(currSum,i);
        //sum[j]%p prev sum
        var prevSum = mod(currSum-total,p);
        if(map.has(prevSum)){
            res = Math.min(res,i-map.get(prevSum));
        }
    }
    return res === nums.length ? -1:res;
};

//use the mod function to help evaluate negative values correctly
var mod = function(a,b){
    // a=(sum[i] -total) , b=p
    // sum[i]%p -total %p = a%b
    var c = a%b;
    // avoid negative value;
    return c < 0 ? c+b:c;
}
// time O(n)
// space O(N)
littlemoon-zh commented 3 years ago

day 24

Delete Sublist to Make Sum Divisible By K

注意这里是删除连续的list,所以为了快速计算这一部分list的相关内容,考虑使用前缀和类似的方法。

如果sum(nums) % k == 0可以直接返回0,否则我们可以得到一个mod = sum(nums) % k,得到这个mod有啥用呢,如果我们发现,有一段list和模k也等于mod,我们就可以把这个list给删除,从而使得剩下的可以被k整除;

理论上是这样,但是在计算时,仍然要做出一些优化,否则没法直接利用前缀和。在计算到某一步的前缀和total时,得到此时的curMod,如果存在某段和的模等于mod,那么之前的那个前缀和的模一定是(curMod - mod + k) % k,只要看看是否存在即可。

class Solution:
    def solve(self, nums, k):
        s = sum(nums)
        mod = s % k
        if mod == 0:
            return 0
        m = {0: -1}
        total = 0
        res = len(nums)

        for i in range(len(nums)):
            total += nums[i]
            curMod = total % k
            desiredMod = (curMod - mod + k) % k
            if desiredMod in m:
                res = min(res, i - m[desiredMod])
            m[curMod] = i

        if res == len(nums):
            return -1
        return res
ghost commented 3 years ago

题目

Delete Sublist to Make Sum Divisible By K Similar to Leetcode: 974. Subarray Sums Divisible by K

思路

Hash table, Prefix sum and modular arithmetic

代码


class Solution:
    def solve(self, nums, k):
        res = len(nums)
        memo = {0:-1}
        target = sum(nums) % k

        curr_sum = 0
        for i in range(len(nums)):
            curr_sum = curr_sum + nums[i]
            mod = curr_sum % k 
            memo[mod] = i
            if (curr_sum - target)%k in memo: 
                res = min(res, i - memo[(curr_sum - target)%k] )

        return res if res != len(nums) else -1         

复杂度

Space: O(N) Time: O(N)

muimi commented 3 years ago

思路

问题可转化为: 求解一个最短的子数组[i, j]的长度,满足条件MOD(SUM(i:j), p) == MOD(SUM(0:n), p)

由于,其中的MOD(SUM(i:j), p) = MOD(SUM(0:i), p) + MOD(SUM(i+1:j), p) 进一步简化为Two Sum问题

代码

// Input:
// [8,32,31,18,34,20,21,13,1,27,23,22,11,15,30,4,2]
// 148
// -----------
// p: 148
// mod: 16
// nums[i]:  8, 32, 31, 18,  34,  20, 21, 13,  1, 27, 23,  22,  11,  15,  30,   4,  2
// curmod:   8, 40, 71, 89, 123, 143, 16, 29, 30, 57, 80, 102, 113, 128,  10,  14, 16
// target: 140, 24, 55, 73, 107, 127,  0, 13, 14, 41, 64,  86,  97, 112, 142, 146,  0

class Solution {
  public int minSubarray(int[] nums, int p) {
    int curmod = 0, mod = 0;
    int n = nums.length, res = n;
    for (int num : nums) mod = (mod + num) % p;
    if (mod == p) return 0;
    Map<Integer, Integer> map = new HashMap<>();
    map.put(0, -1); //necessary, otherwise cannot deal with the case of result subarray begin form 0
    for (int i = 0; i < n; i++) {
      curmod = (curmod + nums[i]) % p;
      map.put(curmod, i);
      int target = (curmod - mod + p) % p;
      res = Math.min(res, i - map.getOrDefault(target, -n));
    }
    return res < n ? res : -1;
  }
}

复杂度

laurallalala commented 3 years ago

代码

class Solution:
    def solve(self, nums, k):
        s = sum(nums)
        mod = s % k
        if mod == 0:
            return 0
        m = {0: -1}
        total = 0
        res = len(nums)

        for i in range(len(nums)):
            total += nums[i]
            curMod = total % k
            desiredMod = (curMod - mod + k) % k
            if desiredMod in m:
                res = min(res, i - m[desiredMod])
            m[curMod] = i

        if res == len(nums):
            return -1
        return res

复杂度

Menglin-l commented 3 years ago

思路:

前缀和+哈希表


代码部分:

class Solution {
    public int minSublist(int[] nums, int k) {
        int len = nums.length;
        int[] preSum = new int[len];
        int sum = 0;
        // 记录前缀和对k的余数
        for (int i = 0; i < len; i ++) {
            sum += nums[i] % k;
            sum %= k;
            preSum[i] = sum;
        }

        if (sum == 0) return 0;

        int res = len; // 最终结果不超过len,定义len为边界
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(sum, -1);
        for (int j = 0; j < len; j ++) {
            int remainder = preSum[j];
            int target = (remainder + sum) % k;
            map.put(target, j);

            if (map.containsKey(remainder)) {
                int size = j - map.get(remainder);
                res = Math.min(res, size);
                map.remove(remainder);
            }
        }

        if (res != len) return res;

        return -1;
    }
}

复杂度:

Time: O(N)

Space: O(N)

Bingbinxu commented 3 years ago

思路 利用数学推导,得到前缀和和整体和关于余树的关系,从而找到余树相等的下标 利用map存储余树 官方代码里有错误当res利用min取最小值,改变了循环的次数,应改掉 map[0] = -1是为了应对删除数是第一个的情况 代码

int solve(vector<int>& nums, int k) {
    int tar = 0;
    for (auto i : nums) {
        tar += i;
    }
    tar = tar % k;
    map<int, int> map;
    map[0] = -1;

    int prefix = 0, res_len = nums.size(), res = nums.size();
    for (int i = 0; i < res_len; i++) {
        prefix += nums[i];
        int mod = prefix % k;
        map[mod] = i;
        int x = prefix - tar;
        int y = ((x % k) + k) % k;
        if (map.count(y)) {
            res = min(res, i - map[y]);            
        }
    }
    return res == nums.size() ? -1 : res;
}

复杂度 时间复杂度:对每个前缀进行遍历O(N) 空间复杂度:O(min(N,k))

itsjacob commented 3 years ago

Intuition

Implementation

class Solution
{
public:
  int minSubarray(vector<int> &nums, int p)
  {
    long sum_all{ 0 };
    for (auto const &n : nums) {
      sum_all += n;
    }
    int sum_all_mod_p = sum_all % p;
    std::unordered_map<int, int> aMap;
    // Need to prepend the prefix sum array with sum value 0 and index -1, so that we can handle the first element
    aMap.emplace(0, -1);
    long sum_i{ 0 };
    int res = nums.size();
    for (int i = 0; i < nums.size(); i++) {
      sum_i += nums[i];
      int sum_i_mod_p = sum_i % p;
      // We can directly update this key,value pair because we are looking for smallest subarray
      aMap[sum_i_mod_p] = i;
      int sum_j_mod_p = sum_i_mod_p - sum_all_mod_p;
      // handle negative values of sum_j_mod_p
      if (sum_j_mod_p < 0) sum_j_mod_p += p;
      if (aMap.count(sum_j_mod_p) > 0) {
        res = std::min(res, i - aMap[sum_j_mod_p]);
      }
    }
    return res == nums.size() ? -1 : res;
  }
};

Complexity