Check if sum of subarray is a multiple of target - python

I encountered this problem, where I have to check if there exists a subarray sum that is a multiple of a target value, and if the length of the subarray is at least 2.
I've looked at one of the solution, which is below
class Solution():
def checkSubarraySum(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
dic = {0:-1}
summ = 0
for i, n in enumerate(nums):
if k != 0:
summ = (summ + n) % k
else:
summ += n
if summ not in dic:
dic[summ] = i
else:
if i - dic[summ] >= 2:
return True
return False
What I don't understand is why it is "i - dic[summ] >= 2" instead of greater or equal to 1? I assume it is checking whether the length is greater than 2, so wouldn't the difference of two indexes plus one be the length of the subarray?

Since all the elements in nums are either 0 or positive, the condition summ in dic (else of summ not in dic) and i - dic[summ] >= 2 only satisfy when the line summ = (summ + n) % k is run in the same iteration. The reason we need to check why it's >=2 and not >=1 is because the index in dic[summ] is not included in the subarray so i - dic[summ] is to check if the size of the continuous subarray is at least 2

Related

Returning smallest positive int that does not occur in given list

Write a function that given an array of A of N int, returns the smallest positive(greater than 0) that does not occur in A.
I decided to approach this problem by iterating through the list after sorting it.
The value of the current element would be compared to the value of the next element. Because the list is sorted, the list should follow sequentially until the end.
However, if there is a skipped number this indicates the smallest number that does not occur in the list.
And if it follows through until the end, then you should just add one to the value of the last element.
def test():
arr = [23,26,25,24,28]
arr.sort()
l = len(arr)
if arr[-1] <= 0:
return 1
for i in range(0,l):
for j in range(1,l):
cur_val = arr[i]
next_val = arr[j]
num = cur_val + 1
if num != next_val:
return num
if num == next_val: //if completes the list with no skips
return arr[j] + 1
print(test())
I suggest that you convert to a set, and you can then efficiently test whether numbers are members of it:
def first_int_not_in_list(lst, starting_value=1):
s = set(lst)
i = starting_value
while i in s:
i += 1
return i
arr = [23,26,25,24,28]
print(first_int_not_in_list(arr)) # prints 1
You can do the following:
def minint(arr):
s=set(range(min(arr),max(arr)))-set(arr)
if len(s)>0:
return min(set(range(min(arr),max(arr)))-set(arr)) #the common case
elif 1 in arr:
return max(arr)+1 #arr is a complete range with no blanks
else:
return 1 #arr is negative numbers only
You can make use of sets to achieve your goal.
set.difference() method is same as relative complement denoted by A – B, is the set of all elements in A that are not in B.
Example:
Let A = {1, 3, 5} and B = {1, 2, 3, 4, 5, 6}. Then A - B = {2, 4, 6}.
Using isNeg() method is used to check whether given set contains any negative integer.
Using min() method on A - B returns the minimum value from set difference.
Here's the code snippet
def retMin(arrList):
min_val = min(arrList) if isNeg(arrList) else 1
seqList=list(range((min_val),abs(max(arrList))+2))
return min(list(set(seqList).difference(arrList)))
def isNeg(arr):
return(all (x > 0 for x in arr))
Input:
print(retMin([1,3,6,4,1,2]))
Output:
5
Input:
print(retMin([-2,-6,-7]))
Output:
1
Input:
print(retMin([23,25,26,28,30]))
Output:
24
Try with the following code and you should be able to solve your problem:
def test():
arr = [3,-1,23,26,25,24,28]
min_val = min(val for val in arr if val > 0)
arr.sort()
l = len(arr)
if arr[-1] <= 0:
return 1
for i in range(0,l):
if arr[i] > 0 and arr[i] <= min_val:
min_val = arr[i] + 1
return min_val
print(test())
EDIT
It seems you're searching for the the value grater than the minimum positive integer in tha array not sequentially.
The code it's just the same as before I only change min_val = 1 to:
min_val = min(val for val in arr if val > 0), so I'm using a lambda expression to get all the positive value of the array and after getting them, using the min function, I'll get the minimum of those.
You can test it here if you want

Leetcode question '3Sum' algorithm exceeds time limit, looking for improvement

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
class Solution:
def threeSum(self, nums):
data = []
i = j = k =0
length = len(nums)
for i in range(length):
for j in range(length):
if j == i:
continue
for k in range(length):
if k == j or k == i:
continue
sorted_num = sorted([nums[i],nums[j],nums[k]])
if nums[i]+nums[j]+nums[k] == 0 and sorted_num not in data:
data.append(sorted_num)
return data
My soulution is working well but it appears that it may be too slow.
Is there a way to improve my codes without changing it significantly?
This is a O(n^2) solution with some optimization tricks:
import itertools
class Solution:
def findsum(self, lookup: dict, target: int):
for u in lookup:
v = target - u
# reduce duplication, we may enforce v <= u
try:
m = lookup[v]
if u != v or m > 1:
yield u, v
except KeyError:
pass
def threeSum(self, nums: List[int]) -> List[List[int]]:
lookup = {}
triplets = set()
for x in nums:
for y, z in self.findsum(lookup, -x):
triplets.add(tuple(sorted([x, y, z])))
lookup[x] = lookup.get(x, 0) + 1
return [list(triplet) for triplet in triplets]
First, you need a hash lookup to reduce your O(n^3) algorithm to O(n^2). This is the whole idea, and the rest are micro-optimizations:
the lookup table is build along with the scan on the array, so it is one-pass
the lookup table index on the unique items that seen before, so it handles duplicates efficiently, and by using that, we keep the iteration count of the second-level loop to the minimal
This is an optimized version, will pass through:
from typing import List
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
unique_triplets = []
nums.sort()
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
lo = i + 1
hi = len(nums) - 1
while lo < hi:
target_sum = nums[i] + nums[lo] + nums[hi]
if target_sum < 0:
lo += 1
if target_sum > 0:
hi -= 1
if target_sum == 0:
unique_triplets.append((nums[i], nums[lo], nums[hi]))
while lo < hi and nums[lo] == nums[lo + 1]:
lo += 1
while lo < hi and nums[hi] == nums[hi - 1]:
hi -= 1
lo += 1
hi -= 1
return unique_triplets
The TLE is most likely for those instances that fall into these two whiles:
while lo < hi and nums[lo] == nums[lo + 1]:
while lo < hi and nums[lo] == nums[lo + 1]:
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.
I'd suggest:
for j in range(i+1, length):
This will save you len(nums)^2/2 steps and first if statement becomes redundant.
sorted_num = sorted([nums[i],nums[j],nums[k]])
if nums[i]+nums[j]+nums[k] == 0 and sorted_num not in data:
sorted_num = sorted([nums[i],nums[j],nums[k]])
data.append(sorted_num)
To avoid unneeded calls to sorted in the innermost loop.
Your solution is the brute force one, and the slowest one.
Better solutions can be:
Assume you start from an element from array. Consider using a Set for finding next two numbers from remaining array.
There is a 3rd better solution as well. See https://www.gyanblog.com/gyan/coding-interview/leetcode-three-sum/

Trying to implement a finder function using recursion in python

I am trying to fine the kth smallest value in x selected using a random splitter,
using recusion, I have already done this without recursion but I am explicitly trying to find it with using recursion. I am faced with a x = x[1,len(x)] ValueError: ValueError: empty range for randrange() (0,0, 0)
splitter = random.randrange(0,len(x))
ants = x[splitter]
lilz = []
bigz = []
for pigs in x:
if pigs >= ants:
bigz.append(pigs)
elif pigs == ants:
splitter
else:
lilz.append(pigs)
if k == splitter:
s = x[splitter]
elif k < splitter:
s = selectR(lilz,k)
else:
s = selectR(bigz, k - ( len(lilz) + 1))
return s
Rather than looping over the values in x and splitting it into two lists, just sort x the first time it's given to us, and then your evaluations become easier. You COULD sort it every time, but if we add a 3rd parameter defaulted to False, we can indicate on recursive calls that it's already sorted and save some work:
import random
def selR(x, k, is_sorted=False):
"""Returns: The kth smallest value in x selected using a random splitter,
using RECURSION.
Parameter x: list of values
Precondition: x contains ints or floats and len(x) > 0
Parameter k: the rank of the value we want to find
Precondition: k is an int in [1..n] where n is the length of x."""
if not is_sorted:
x = sorted(x)
if len(x) == 1 or k <= 1:
"""
1) Does x only have one item? Then it doesn't
really matter what k is, we're done.
2) Is k <= 1? If so, then we don't need to split anymore -
the head of the sorted list is the desired value.
"""
return x[0]
"""3) Find a splitter in the defined range - [1..n] where n is the length of x"""
splitter = random.randrange(1,len(x))
if splitter == k:
"""
4) Is splitter the same as k? If so, we've found our value;
just return the tail of split list. return x[k-1]
"""
return x[k-1]
elif splitter > k:
"""
5) Is splitter larger than k? If so, the kth smallest value is found
before the split, so we recurse with selR(x[1:splitter], k-1) - we
can start at x[1] and reduce k by one, because if x[0] was the
kth smallest value, we would have already returned it in step 2.
"""
return selR(x[1:splitter], k-1, True)
else:
"""
6) Is splitter smaller than k? Then the kth smallest value is found
after the split return selR(x[splitter:], k-len(x[0:splitter])) -
that is, remove items before splitter from x and reduce k by the number
of items we just removed.
"""
return selR(x[splitter:], k-len(x[0:splitter]), True)
# Test case
x = [1,2,1,3,4,5]
for k in range(1,len(x)+1):
ord = { 1: "st", 2: "nd", 3: "rd" }.get(k if (k < 20) else (k % 10), 'th')
print(f"{k}{ord} smallest element of {x}:", selR(x, k))

Target sum dp algorithm when element can be zero

Target sum prompt:
You are given a set of positive numbers and a target sum ‘S’. Each number should be assigned either a ‘+’ or ‘-’ sign. We need to find the total ways to assign symbols to make the sum of the numbers equal to the target ‘S’.
Input: {1, 1, 2, 3}, S=1
Output: 3
Explanation: The given set has '3' ways to make a sum of '1': {+1-1-2+3} & {-1+1-2+3} & {+1+1+2-3}
let’s say ‘Sum(s1)’ denotes the total sum of set ‘s1’, and ‘Sum(s2)’ denotes the total sum of set ‘s2’. Add negative sign to set 's2'
This equation can be reduced to the subset sum problem target + sum(nums)/2
sum(s1) - sum(s2) = target
sum(s1) + sum(s2) = sum(nums)
2 * sum(s1) = target + sum(nums)
sum(s1) = target + sum(nums) / 2
def findTargetSumWays(nums, S):
"""
:type nums: List[int]
:type S: int
:rtype: int
"""
if (sum(nums) + S) % 2 == 1 or sum(nums) < S:
return 0
ssum = (sum(nums) + S) // 2
dp = [[0 for _ in range(ssum + 1)] for _ in range(len(nums))]
# col == 0
for i in range(len(nums)):
# [] or [0]
if i == 0 and nums[i] == 0:
dp[i][0] = 2
# [] or [0] from previous
elif nums[i] == 0:
dp[i][0] = 2 * dp[i-1][0]
else: # empty set only
dp[i][0] = 1
# take 1st element nums[0] in s == nums[0]
for s in range(1, ssum + 1):
if nums[0] == s:
dp[0][s] = 1
for i in range(1, len(nums)):
for s in range(1, ssum + 1):
if nums[i] != 0:
# skip element at i
dp[i][s] = dp[i - 1][s]
# include element at i
if s >= nums[i]:
dp[i][s] += dp[i - 1][s - nums[i]]
else: # nums[i] = 0
dp[i][s] = dp[i-1][s] * 2
return dp[len(nums) - 1][ssum]
I've spent a few hours on this prompt but still couldn't pass the following example
[7,0,3,9,9,9,1,7,2,3]
6
expected: 50
output: 43 (using my algorithm)
I've also looked through other people's answers here, they all makes sense but I just want to know where could I have possibly missed in my algorithm here?
You can do it like this:
from itertools import product
def findTargetSumWays(nums, S):
a = [1,-1]
result=[np.multiply(nums,i) for i in list(product(a, repeat=len(nums))) if sum(np.multiply(nums,i))==S]
return(len(result))
findTargetSumWays(inputs,6)
50
Basically I get all possible combinations of -1,1 in tuples with the size the same as input elements and then I'm multiplying these tuples with input.
I ran into this same issue when handling zeroes but I did this on C++ where I handled zeroes seperately.
Make sure that in the knapsack approach skip zeroes i.e.
if(a[i-1] == 0)
dp[i][j] = dp[i-1][j];
We can handle zeroes seperately by simply counting the zero occurences and we can put them in either S1 or S2. So, for each zero it is 2*(answer) and for n zeroes its 2^n * (answer) i.e.
answer = pow(2, num_zero) * answer;
Also, don't forget to simply return zero if sum(nums) + target is odd as S1 can't be fractional or target is greater than sum(nums) i.e.
if(sum < target || (sum+target)%2 == 1)
return 0;
The overall code looks like this:
int subsetSum(int a[], int n, int sum) {
int dp[n+1][sum+1];
for(int i = 0; i<sum+1; i++)
dp[0][i] = 0;
for(int i = 0; i<n+1; i++)
dp[i][0] = 1;
for(int i = 1; i<n+1; i++) {
for(int j = 1; j<sum+1; j++) {
if(a[i-1] == 0)
dp[i][j] = dp[i-1][j];
else if(a[i-1]<=j)
dp[i][j] = dp[i-1][j-a[i-1]] + dp[i-1][j];
else
dp[i][j] = dp[i-1][j];
}
}
return dp[n][sum]; }
int findTargetSumWays(int a[], int target) {
int sum = 0;
int num_zero = 0;
for(int i = 0; i<a.size(); i++) {
sum += a[i];
if(a[i] == 0)
num_zero++;
}
if(sum < target || (sum+target)%2 == 1)
return 0;
int ans = subsetSum(a, a.size(), (sum + target)/2);
return pow(2, num_zero) * ans;
}
The source of the problem is this part, initializing col == 0:
# col == 0
for i in range(len(nums)):
# [] or [0]
if i == 0 and nums[i] == 0:
dp[i][0] = 2
# [] or [0] from previous
elif nums[i] == 0:
dp[i][0] = 2 * dp[i-1][0]
else: # empty set only
dp[i][0] = 1
This code treats zeros differently depending on how the list is ordered (it resets the value to 1 if it hits a nonzero value). It should instead look like this:
# col == 0
for i in range(len(nums)):
# [] or [0]
if i == 0 and nums[i] == 0:
dp[i][0] = 2
elif i == 0:
dp[i][0] = 1
# [] or [0] from previous
elif nums[i] == 0:
dp[i][0] = 2 * dp[i-1][0]
else: # empty set only
dp[i][0] = dp[i - 1][0]
This way, the first value is set to either 2 or 1 depending on whether or not it's zero, and nonzero values later in the list don't reset the value to 1. This outputs 50 in your sample case.
You can also remove room for error by giving simpler initial conditions:
def findTargetSumWays(nums, S):
"""
:type nums: List[int]
:type S: int
:rtype: int
"""
if (sum(nums) + S) % 2 == 1 or sum(nums) < S:
return 0
ssum = (sum(nums) + S) // 2
dp = [[0 for _ in range(ssum + 1)] for _ in range(len(nums) + 1)]
# col == 0
dp[0][0] = 1
for i in range(len(nums)):
for s in range(ssum + 1):
dp[i + 1][s] = dp[i][s]
if s >= nums[i]:
dp[i + 1][s] += dp[i][s - nums[i]]
return dp[len(nums)][ssum]
This adds an additional row to represent the state before you add any numbers (just a 1 in the top left corner), and it runs your algorithm on the rest of the rows. You don't need to initialize anything else or treat zeros differently, and this way it should be easier to reason about the code.
The issue with your function is related to the way you manage zero values in the list. Perhaps a simpler way for you to handle the zero values would be to exclude them from the process and then multiply your resulting count by 2**Z where Z is the number of zero values.
While trying to find the problem, I did a bit of simplification on your code and ended up with this: (which gives the right answer, even with zeroes in the list).
ssum = (sum(nums) + S) // 2
dp = [1]+[0]*ssum # number of sets that produce each sum from 0 to ssum
for num in nums:
for s in reversed(range(num,ssum + 1)):
dp[s] += dp[s-num]
return dp[ssum]
What I did was:
Eliminate a dimension in dp because you don't need to keep all the previous set counts. Only the current and next one. Actually it can work using only the current set counts if you process the sum values backwards from ssum down to zero (which i did).
The condition s >= nums[i]was eliminated by starting the s range from the current num value so that the index s - num can never be negative.
With that done, there was no need for an index on nums, I could simply go through the values directly.
Then I got rid of all the special conditions on zero values by initializing dp with 1 for the zero sum (i.e. initially an empty set is the one solution to obtain a sum of zero, then increments proceed from there).
Starting with the empty set baseline allows the progressive accumulation of set counts to produce the right result for all values without requiring any special treatment of zeroes. When num is zero it will naturally double all the current set counts because dp[s] += dp[s-0] is the same as dp[s] = 2 * dp[s]. If the list starts out with a zero then the set count for a sum of zero (dp[0]) will be doubled and all subsequent num values will have a larger starting count (because they start out from the dp[0] value initialized with 1 for the empty set).
With that last change, the function started to give the right result.
My assertion is that, because your solution was not starting from the "empty set" baseline, the zero handling logic was interfering with the natural progression of set counts. I didn't try to fine tune the zero conditions because they weren't needed and it seemed pointless to get them to arrive at the same states that a mere initialization "one step earlier" would produce
From there, the logic can be further optimized by avoiding assignments do dp[s] outside the range of minimum and maximum possible sums (which "slides" forward as we progress through the nums list):
ssum = (sum(nums) + S) // 2
dp = [1]+[0]*ssum
maxSum = 0
minSum = S - ssum # equivalent to: ssum - sum(nums)
for num in nums:
maxSum += num
minSum += num
for s in reversed(range(max(num,minSum),min(ssum,maxSum)+1)):
dp[s] += dp[s-num]
return dp[ssum]

How to apply recursion to this code about the number of ways to sum up to 'N'?

Given a list of integers, and a target integer N, I want to find the number of ways in which the integers in the list can be added to get N. Repetition is allowed.
This is the code:
def countWays(arr, m, N):
count = [0 for i in range(N + 1)]
# base case
count[0] = 1
# Count ways for all values up
# to 'N' and store the result
# m=len(arr)
for i in range(1, N + 1):
for j in range(m):
# if i >= arr[j] then
# accumulate count for value 'i' as
# ways to form value 'i-arr[j]'
if (i >= arr[j]):
count[i] += count[i - arr[j]]
# required number of ways
return count[N]
(from Geeksforgeeks)
Any idea on how to do it using recursion and memoization?
The problem you are trying to solve is the same as the number of ways to make a change for an amount given a list of denominations. In your case, the amount is analogous to target number N and the denominations are analogous to the list of integers. Here is the recursive code. The link is https://www.geeksforgeeks.org/coin-change-dp-7/
# Returns the count of ways we can sum
# arr[0...m-1] coins to get sum N
def count(arr, m, N ):
# If N is 0 then there is 1
# solution (do not include any coin)
if (N == 0):
return 1
# If N is less than 0 then no
# solution exists
if (N < 0):
return 0;
# If there are no coins and N
# is greater than 0, then no
# solution exist
if (m <=0 and N >= 1):
return 0
# count is sum of solutions (i)
# including arr[m-1] (ii) excluding arr[m-1]
return count( arr, m - 1, N ) + count( arr, m, N-arr[m-1] );

Categories