Open azl397985856 opened 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;
}
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)
前缀和变形
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
}
思路: 前缀和 + 哈希,利用同余定理
哈希表<前缀和的余数,整数下标>:key: 前i个整数和的余数(sum % p), value: 整数i
复杂度分析:
代码(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;
}
};
HashMap。
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;
}
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;
}
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
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)
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
.
int mod(int a, int b) {
int remainder = a % b;
while (remainder < 0) remainder += b;
return remainder;
}
int solve(vector
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)`
https://binarysearch.com/problems/Delete-Sublist-to-Make-Sum-Divisible-By-K
A%k == B%k
then (A - B)%k == 0
.totalSum%k == sum[i, j]%k
, so that (totalSum - sum[i, j])%k == 0
.sum[i, j] = presum[j + 1] - presum[i]
. presum[i + 1] = presum[i] + nums[i]
.totalSum%k == (presum[j+1]-presum[i])%k
==> (presum[j+1] - totalsum)%k == presum[i]%k
. The problem is converted to find the i, j that (presum[j+1] - totalsum)%k == presum[i]%k
.Math.floorMod(-1, 4)
.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;
}
}
python
class Solution:
def solve(self, nums, k):
tar = sum(nums)
tar %= k
if k == 1 or tar == 0:
return 0
d = collections.defaultdict(int)
d[0] = -1
prefix = 0
res = len(nums)
for i, num in enumerate(nums):
prefix += num
mod = prefix % k
prev_mod = (prefix - tar - (prefix - tar) // k * k) % k
if prev_mod in d:
res = min(res, i - d[prev_mod])
d[mod] = i
return res if res < len(nums) else -1
思路: 用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)
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)
https://binarysearch.com/problems/Delete-Sublist-to-Make-Sum-Divisible-By-K
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)
即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)
总觉得这道题的关键在于能想到 (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)
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
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 为数组长度。
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; } }
使用前缀和+ 哈希表。做了好几道前缀和的题,重点在于找到当前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)
思路
太难了..
代码
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))
前缀和,哈希表
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))
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;
}
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;
}
}
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)
语言: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;
}
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;
}
}
看题解复现
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
抄下
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;
}
}
非常难的一道题,看了很多小伙伴的答案,选了一个最能理解的抄的。谢谢各位的思路。
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
思路: 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))
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;
}
暴力解法
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)
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))
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
前缀和+同余定理+哈希表。此题要求连续的子数组,适合使用前缀和。先求出数组总和,然后模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;
}
}
复杂度分析
根据数组的前缀和,计算前缀和的余数,并使用哈希表保存余数对应的数组下标
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;
}
}
【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
复杂度:
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;
}
};
copy from others
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
先计算整个的和 mod k的值 然后计算每个prefix sum mod k的值 如果pref_sum mod k和 整体和mod k的差在之前出现就更新最小的length
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
time O(n)
space O(min(n, k))
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;
}
}
//(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)
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
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)
问题可转化为: 求解一个最短的子数组[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;
}
}
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
代码部分:
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;
}
}
思路 利用数学推导,得到前缀和和整体和关于余树的关系,从而找到余树相等的下标 利用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))
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;
}
};
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.