I am working on this coding challenge named Similarity Measure. Now the problem is my code works fine for some test cases, and failed due to the Time Limit Exceed problem. However, my code is not wrong, takes more than 25 sec for input of range 10^4.
I need to know what I can do to make it more efficient, I cannot think on any better solution than my code.
Question goes like this:
Problems states that given an array of positive integers, and now we have to answer based upon the Q queries.
Query: Given two indices L,R, determine the maximum absolute difference of index of two same elements lies between L and R
If in a range, there are no two same inputs then return 0
INPUT FORMAT
The first line contains N, no. of elements in the array A
The Second line contains N space separated integers that are elements of the array A
The third line contains Q the number of queries
Each of the Q lines contains L, R
CONSTRAINTS
1 <= N, Q <= 10^4
1 <= Ai <= 10^4
1 <= L, R <= N
OUTPUT FORMAT
For each query, print the ans in a new line
Sample Input
5
1 1 2 1 2
5
2 3
3 4
2 4
3 5
1 5
Sample Output
0
0
2
2
3
Explanation
[2,3] - No two elements are same
[3,4] - No two elements are same
[2,4] - there are two 1's so ans = |4-2| = 2
[3,5] - there are two 2's so ans = |5-3| = 2
[1,5] - there are three 1's and two 2's so ans = max(|4-2|, |5-3|, |4-1|, |2-1|) = 3
Here is my algorithm:
To take the input and test the range in a different method
Input will be L, R and the Array
For difference between L and R equal to 1, check if the next element is equal, return 1 else return 0
For difference more than 1, loop through array
Make a nested loop to check for the same element, if yes, store the difference into maxVal variable
Return maxVal
My Code:
def ansArray(L, R, arr):
maxVal = 0
if abs(R - L) == 1:
if arr[L-1] == arr[R-1]: return 1
else: return 0
else:
for i in range(L-1, R):
for j in range(i+1, R):
if arr[i] == arr[j]:
if (j-i) > maxVal: maxVal = j-i
return maxVal
if __name__ == '__main__':
input()
arr = (input().split())
for i in range(int(input())):
L, R = input().split()
print(ansArray(int(L), int(R), arr))
Please help me with this. I really want to learn a different and a more efficient way to solve this problem. Need to pass all the TEST CASES. :)
You can try this code:
import collections
def ansArray(L, R, arr):
dct = collections.defaultdict(list)
for index in range(L - 1, R):
dct[arr[index]].append(index)
return max(lst[-1] - lst[0] for lst in dct.values())
if __name__ == '__main__':
input()
arr = (input().split())
for i in range(int(input())):
L, R = input().split()
print(ansArray(int(L), int(R), arr))
Explanation:
dct is a dictionary that for every seen number keeps a list of indices. The list is sorted so lst[-1] - lst[0] will give maximum absolute difference for this number. Applying max to all this differences you get the answer. Code complexity is O(R - L).
This can be solved as O(N) approximately the following way:
from collections import defaultdict
def ansArray(L, R, arr) :
# collect the positions and save them into the dictionary
positions = defaultdict(list)
for i,j in enumerate(arr[L:R+1]) :
positions[j].append(i)
# create the list of the max differences in index
max_diff = list()
for vals in positions.values() :
max_diff.append( max(vals) - min(vals) )
# now return the max element from the list we have just created
if len(max_diff) :
return max(max_diff)
else :
return 0
Related
I have attempted google's kickstart 2020 challenge. Round C problem 1 has me stumped for some. I have tried many different ways of completing the challenge. The problem looks easy but I can't complete it. The problem is that I do not understand what I am doing wrong. Please point me in the right direction or point the issue in with my code.
Problem
Google Kickstart 2020 - Round C | Problem 1
https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ff43/00000000003380d2
Avery has an array of N positive integers. The i-th integer of the array is Ai.
A contiguous subarray is an m-countdown if it is of length m and contains the integers m, m-1, m-2, ..., 2, 1 in that order. For example, [3, 2, 1] is a 3-countdown.
Can you help Avery count the number of K-countdowns in her array?
Input
The first line of the input gives the number of test cases, T. T test cases follow. Each test case begins with a line containing the integers N and K. The second line contains N integers. The i-th integer is Ai.
Output
For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the number of K-countdowns in her array.
Pseudocode
get the number of cases
Loop in range(number of cases):
get the N (number of elements), K(initial countdown value)
get the array of values
generate an array of the countdown sequence [K ... 1] - signature
counter = 0
Loop elem in range(Number of elements):
if elem == K:
if there is space to slice the array (length of signature) - possible signature
if possible signature == signature:
counter += 1
print(counter)
Python 3 Code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
noc = int(input('')) # getting the number of cases # NOC- number of cases
# Loop over the # of cases
for c in range(noc):
(N, K) = [int(i) for i in input('').split(' ')] # getting N, K
# N - number of elements given
# K - initial countdown value
# getting the elements
caseList = [int(i) for i in input('').split(' ')]
# generating a 'signature' or list of factorial for the countdown
steps = [i for i in range(1, K + 1)][::-1]
# counter for number of matches
countdown = 0 # init value
# loop over each element i n list
for i in range(N):
# if element == K(init countdown number)
if caseList[i] == K:
# make sure there is space to get the sliced array
if i <= len(caseList) - len(steps):
# get the next m numbers if
if caseList[i:i + len(steps)] == steps:
countdown += 1 # increment
print countdown # print the number of matches
Your solution seems fine, except that the output isn't as specified and not for Python 3, but 2, simply change it to:
print(f'Case {c}: {countdown}')
Apart from that, you're doing a bit more work than is needed. You really only need to go through the entire list once to count K-countdowns.
For example:
import sys
from io import StringIO
sys.stdin = StringIO('3\n2 2\n2 1\n8 2\n0 2 1 2 2 1 2 1 0\n0 2\n\n')
t = int(input())
for c in range(t):
(n, k) = [int(i) for i in input().split()]
a = [int(i) for i in input().split()]
# initialise goal, position in array and count
goal, i, count = k, 0, 0
while i < n:
# if item in current position matches current goal
if a[i] == goal:
# then next goal item is one less
goal -= 1
# until all in K-countdown were found
if goal == 0:
# then start over and increase count
goal = k
count += 1
# look at the next position
i += 1
# else (current item doesn't match goal), if already looking for start of K-countdown
elif goal == k:
# look at the next position
i += 1
# else (current item doesn't match goal, goal wasn't start of K-countdown)
else:
# look for start of K-countdown
goal = k
print(f'Case #{c}: {count}')
I don't find any issue with your solution. Might be something your output format.
You are supposed to output in the form of Case #x: y, where x is the test case number (starting from 1) and y is the number of K-countdowns in her array.
Example:
Case #1: 2
Case #2: 0
Case #3: 1
Note: Make sure you are using Python 2.x if you are using print x instead of print(x)
I was wondering the same as well.
Lets look at the constraint given:
1 ≤ T ≤ 100. # Testcases
2 ≤ K ≤ N. # Value of K
1 ≤ Ai ≤ 2 × 105, for all i. # Index- i
# Test Set 1
2 ≤ N ≤ 1000.
# Test Set 2
2 ≤ N ≤ 2 × 105 for at most 10 test cases.
For the remaining cases, 2 ≤ N ≤ 1000.
Now suppose we have a testcase
nums = [1]
k = 1
One might think for K=1 the countdown= 1 right ? Actually No.
Read carefully, 2<=N, which means,
Array length must be of minimum length=2.
Expected result,
nums = [1]
K = 1
coutdown = 0
when the constraint already says 2<=N
doesn't it mean that there will be no test case with array length = 0 or 1
There is no issue in #MFK34 except print() requires brackets in python 3 and he prints the answer immediately at end of loop and solution is not as expected. below is my revised solution.
#!/usr/bin/python
# -*- coding: utf-8 -*-
noc = int(input('')) # getting the number of cases # NOC- number of cases
op = []
# Loop over the # of cases
for c in range(noc):
(N, K) = [int(i) for i in input('').split(' ')] # getting N, K
caseList = [int(i) for i in input('').split(' ')]
steps = [i for i in range(1, K + 1)][::-1]
# counter for number of matches
countdown = 0 # init value
# loop over each element i n list
for i in range(N):
# if element == K(init countdown number)
if caseList[i] == K:
# make sure there is space to get the sliced array
if i <= len(caseList) - len(steps):
# get the next m numbers if
if caseList[i:i + len(steps)] == steps:
countdown += 1 # increment
op.append(countdown)
for i,d in enumerate(op):
print("Case #"+str(i+1)+":",d)
I have just stored the results in an array and later printed at end of inputs in order expected.
The question is here:
https://www.hackerrank.com/challenges/equal-stacks/problem
I am getting a termination error due to time out!
Please help me out.
Algorithm explanation:
Step I.
get the 3 array and reverse them, create a new array out of an existing array with each element is sum of all the previous elements. eg: [3,2,1,1,1] -> [1,1,1,2,3] -> [1,2,3,5,8]
So the 3 new array formed would be [1,2,3,5,8] [2,5,9] [1,5,6,7]
Step II.
Again reverse the array [8,5,3,2,1] [9,5,2] [7,6,5,1]
Step III.
Take the smallest array i.e. [9,5,2] traverse the smallest array and search element in the other 2 array - if the element is existing in other 2 array, STOP there and return the number.
Eg. Here I start with elem - 9 : Which is not existing in other 2 array. Next I start with elem - 5 : it is existing in other 2 array.
Hence the answer is 5.
import os
import sys
def equalStacks(h1, h2, h3):
new_h1=[]
new_h2=[]
new_h3=[]
h1=list(reversed(h1))
h2=list(reversed(h2))
h3=list(reversed(h3))
new_h1.append(h1[0])
new_h2.append(h2[0])
new_h3.append(h3[0])
bol=False
ans=-1
for i in range(len(h1)-1):
new_h1.append(new_h1[i]+h1[i+1])
for i in range(len(h2)-1):
new_h2.append(new_h2[i]+h2[i+1])
for i in range(len(h3)-1):
new_h3.append(new_h3[i]+h3[i+1])
low=min([n1,n2,n3])
if low==n1:
for i in list(reversed(new_h1)):
if (i in list(reversed(new_h2))) and (i in list(reversed(new_h3))):
bol=True
ans=i
break
else:
pass
elif low==n2:
for i in list(reversed(new_h2)):
if (i in list(reversed(new_h1))) and (i in list(reversed(new_h3))):
bol=True
ans=i
break
else:
pass
else:
for i in list(reversed(new_h2)):
if (i in list(reversed(new_h1))) and (i in list(reversed(new_h3))):
bol=True
ans=i
break
else:
pass
if bol==True:
return(print(i))
else:
return(print(0))
if __name__ == '__main__':
n1N2N3 = input().split()
n1 = int(n1N2N3[0])
n2 = int(n1N2N3[1])
n3 = int(n1N2N3[2])
h1 = list(map(int, input().rstrip().split()))
h2 = list(map(int, input().rstrip().split()))
h3 = list(map(int, input().rstrip().split()))
result = equalStacks(h1, h2, h3)
I am getting a timeout error....please help me to optimize this code.
If the stacks aren't the same height, then at least one is shorter than the tallest one. The shortest one can't be made taller, so the tallest one must be made shorter.
Pop an item off the tallest stack.
Repeat until they are all the same height.
One possible solution with itertools:
data = '''
3 2 1 1 1
4 3 2
1 1 4 1
'''
from itertools import accumulate, chain, groupby
# prepare the data
data = [[*map(int, line.split())] for line in data.splitlines() if line.strip()]
max_height = 0
for v, g in groupby( sorted(chain(*[accumulate(stack[::-1]) for stack in data]), reverse=True) ):
if sum(1 for _ in g) == 3:
max_height = v
break
print(max_height)
Prints:
5
Simple solution.
Ping me if you are not able to understand.
Here simply apply greedy approach to remove the minimum height cylinder from stack one by one until we get maximum equal height.
def equalStacks(h1, h2, h3):
h1.reverse()
h2.reverse()
h3.reverse()
i = sum(h1)
j = sum(h2)
k = sum(h3)
while i != 0 or j !=0 or k!= 0:
x = min(i,j,k)
if i > x:
i -= h1.pop()
if j > x :
j -= h2.pop()
if k > x:
k -= h3.pop()
if i == j and j == k:
return i
return 0
Hello guys here is the problem. I have something like this in input [[1,2,3],[4,5,6],[7,8,9]]...etc
And i want to generate all possible combination of product of those list and then multiply each elements of the resulting combination beetween them to finally filter the result in a interval.
So first input a n list [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]...etc
Which will then give (1,4,7,10)
(1,4,7,11)
(1,4,7,12)
and so on
Then combination of those result for k in n like (1,4,7)(1,4,10)(1,7,10) for the first row
The multiplication of x as 1*4*7 = 28, 1*4*10 = 40, 1*7*10 = 70
And from this get only the unique combination and the result need in the interval choosed beforehand : if x > 50 and x < 100 i will get (1,7,10) : 70
I did try
def mult(lst): #A function mult i'm using later
r = 1
for element in lst:
r *= element
return round(r)
s = [] #Where i add my list of list
for i in range(int(input1)):
b = input("This is line %s : " % (i+1)).split()
for i in range(len(b)):
b[i] = float(b[i])
s.append(b)
low_result = input("Expected low_result : ")
high_result = input("Expected high_result : ")
combine = []
my_list = []
for element in itertools.product(*s):
l= [float(x) for x in element]
comb = itertools.combinations([*l], int(input2))
for i in list(comb):
combine.append(i)
res = mult(i)
if res >= int(low_result) and res <= int(high_result):
my_list.append(res)
f = open("list_result.txt","a+")
f.write("%s : result is %s\n" % (i, res))
f.close()
And it always result in memory error cause there is too many variation with what i'm seeking.
What i would like is a way to generate from a list of list of 20 elements or more all the product and resulting combination of k in n for the result(interval) that i need.
As suggested above, I think this can be done without exploding your memory by never holding an array in memory at any time. But the main issue is then runtime.
The maths
As written we are:
Producing every combination of m rows of n items n ** m
Then taking a choice of c items from those m values C(m, c)
This is very large. If we have m=25 rows, of n=3 items each and pick c=3 items in them we get:
= n ** m * C(m, c)
= 3 ** 25 * 2300 - n Choose r calculator
= 1.948763802×10¹⁵
If instead we:
Choose c rows from the m rows: C(m, c) as before
Then pick every combination of n items from these c rows: n ** c
With m=25 rows, of n=3 items each and pick c=3 items in them we get:
= n ** c * C(m, c)
= 3 ** 3 * 2300
= 20700
This is now a solvable problem.
The code
from itertools import product, combinations
def mult(values, min_value, max_value):
"""
Multiply together the values, but return None if we get too big or too
small
"""
output = 1
for value in values:
output *= value
# Early return if we go too big
if output > max_value:
return None
# Early return if we goto zero (from which we never return)
if output == 0 and min_value != 0:
return None
if output < min_value:
return None
return output
def yield_valid_combos(values, choose, min_value, max_value):
# No doubt an even fancier list compression would get this too
for rows in combinations(values, choose):
for combos in product(*rows):
value = mult(combos, min_value, max_value)
if value is not None:
yield combos, value
values = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
with open('list_result.txt', 'w') as fh:
for selection, value in yield_valid_combos(
values, choose=3, min_value=50, max_value=100):
fh.write('{}: result is {}\n'.format(selection, value))
This solution also returns no duplicate answers (unless the same value appears in multiple rows).
As an optimisation the multiplication method attempts to return early if we detect the result will be too big or small. We also only open the file once and then keep adding rows to it as they come.
Further optimisation
You can also optimise your set of values ahead of time by screening out values which cannot contribute to a solution. But for smaller values of c, you may find this is not even necessary.
The smallest possible combination of values is c items from the set of the smallest values in each row. If we take the c - 1 smallest items from the set of smallest values, mutliply them together and then divide the maximum by this number, it gives us an upper bound for the largest value which can be in a solution. We can then then screen out all values above this value (cutting down on permutations)
I need to write a function that returns the number of ways of reaching a certain number by adding numbers of a list. For example:
print(p([3,5,8,9,11,12,20], 20))
should return:5
The code I wrote is:
def pow(lis):
power = [[]]
for lst in lis:
for po in power:
power = power + [list(po)+[lst]]
return power
def p(lst, n):
counter1 = 0
counter2 = 0
power_list = pow(lst)
print(power_list)
for p in power_list:
for j in p:
counter1 += j
if counter1 == n:
counter2 += 1
counter1 == 0
else:
counter1 == 0
return counter2
pow() is a function that returns all of the subsets of the list and p should return the number of ways to reach the number n. I keep getting an output of zero and I don't understand why. I would love to hear your input for this.
Thanks in advance.
There are two typos in your code: counter1 == 0 is a boolean, it does not reset anything.
This version should work:
def p(lst, n):
counter2 = 0
power_list = pow(lst)
for p in power_list:
counter1 = 0 #reset the counter for every new subset
for j in p:
counter1 += j
if counter1 == n:
counter2 += 1
return counter2
As tobias_k and Faibbus mentioned, you have a typo: counter1 == 0 instead of counter1 = 0, in two places. The counter1 == 0 produces a boolean object of True or False, but since you don't assign the result of that expression the result gets thrown away. It doesn't raise a SyntaxError, since an expression that isn't assigned is legal Python.
As John Coleman and B. M. mention it's not efficient to create the full powerset and then test each subset to see if it has the correct sum. This approach is ok if the input sequence is small, but it's very slow for even moderately sized sequences, and if you actually create a list containing the subsets rather than using a generator and testing the subsets as they're yielded you'll soon run out of RAM.
B. M.'s first solution is quite efficient since it doesn't produce subsets that are larger than the target sum. (I'm not sure what B. M. is doing with that dict-based solution...).
But we can enhance that approach by sorting the list of sums. That way we can break out of the inner for loop as soon as we detect a sum that's too high. True, we need to sort the sums list on each iteration of the outer for loop, but fortunately Python's TimSort is very efficient, and it's optimized to handle sorting a list that contains sorted sub-sequences, so it's ideal for this application.
def subset_sums(seq, goal):
sums = [0]
for x in seq:
subgoal = goal - x
temp = []
for y in sums:
if y > subgoal:
break
temp.append(y + x)
sums.extend(temp)
sums.sort()
return sum(1 for y in sums if y == goal)
# test
lst = [3, 5, 8, 9, 11, 12, 20]
total = 20
print(subset_sums(lst, total))
lst = range(1, 41)
total = 70
print(subset_sums(lst, total))
output
5
28188
With lst = range(1, 41) and total = 70, this code is around 3 times faster than the B.M. lists version.
A one pass solution with one counter, which minimize additions.
def one_pass_sum(L,target):
sums = [0]
cnt = 0
for x in L:
for y in sums[:]:
z = x+y
if z <= target :
sums.append(z)
if z == target : cnt += 1
return cnt
This way if n=len(L), you make less than 2^n additions against n/2 * 2^n by calculating all the sums.
EDIT :
A more efficient solution, that just counts ways. The idea is to see that if there is k ways to make z-x, there is k more way to do z when x arise.
def enhanced_sum_with_lists(L,target):
cnt=[1]+[0]*target # 1 way to make 0
for x in L:
for z in range(target,x-1,-1): # [target, ..., x+1, x]
cnt[z] += cnt[z-x]
return cnt[target]
But order is important : z must be considered descendant here, to have the good counts (Thanks to PM 2Ring).
This can be very fast (n*target additions) for big lists.
For example :
>>> enhanced_sum_with_lists(range(1,100),2500)
875274644371694133420180815
is obtained in 61 ms. It will take the age of the universe to compute it by the first method.
from itertools import chain, combinations
def powerset_generator(i):
for subset in chain.from_iterable(combinations(i, r) for r in range(len(i)+1)):
yield set(subset)
def count_sum(s, cnt):
return sum(1 for i in powerset_generator(s) if sum(k for k in i) == cnt)
print(count_sum(set([3,5,8,9,11,12,20]), 20))
Not sure if it's the best title. The explanation of what the program is suposed to do is below, my version only works with the first example but it doesn't work in the second when you get for example 1 1 3 1 1 2 because i can't figure out a good way to handle so much variations especially if K is bigger than 3 and the limit is 50. My version:
N, K, M = map(int, input().split())
niz = list(map(int, input().split()))
nizk = list(range(1, K+1))
izlazi = []
for r in range(0, M):
operacija = list(map(int, input().split()))
index = 0
if operacija[0] == 2:
nizkk = []
for z in range(0, len(nizk)):
if nizk[z] in niz:
continue
else:
izlazi.append(-1)
break
for p in range(0, N):
if niz[p] not in nizkk:
nizkk.append(niz[p])
nizkk.sort()
if nizkk == nizk:
index = p
izlazi.append(index+1)
break
else:
continue
else:
index, repl = map(int, operacija[1:])
niz[index - 1] = repl
print(izlazi)
In the first line of the input there should be N, K, M (1 <= N, M <= 100k, 1 <= K <= 50, you don't need to actually check this the numbers that are tested will always be in those ranges). In the second line of input you put a list of numbers which are the lenght of N you entered earlier. M is the number of operations you will do in the following lines of input. There can be 2 operations. If you enter 1 p v(p = index of number you want to replace, v the number you replace it with) or if you enter 2 it needs to find the shortest array of numbers defined by range(1, K+1) in the list of numbers you entered in line 2 and possibly changed with operation 1. If it doesn't exist it should output -1 if it does it should output lenght of numbers in the array you look in(numbers can be like 2, 1, 3 if you're looking for 1, 2, 3, also if you're looking for 1, 2, 3 etc and you have 2, 1, 1, 3 as the shortest one that is the solution and it's lenght is 4). Also the replacement operation doesnt count from 0 but from 1. So watch out when managing lists.
These are the examples you can input in the program ulaz = input, izlaz = ouput:
I have the following idea:
Min length sequence either starts from first element or does not contain first element and hence equals to min length of the same sequence without first element.
So we have recursion here.
For sequence [1,1,3,2,1,1] and [1,2,3] we will have:
Min length from start element [1,1,3,2,1,1] is 4
Min length from start element __[1,3,2,1,1] is 3
Min length from start element ____[3,2,1,1] is 3
Min length from start element ______[2,1,1] is -1
Can stop here.
Result is minimum for [4,3,3] = 3
You have already implemented the part for min length, if it starts from the first element. Need now extract it as a function and create a recursive function.
Some metacode:
function GetMinLength(seq)
{
minLengthFromFirstElement = GetMinLenthFromFirstElement(seq)
minLengthFromRest = GetMinLength(seq[1:]) //recusive call
return Min(minLengthFromFirstElement, minLengthFromRest )//-1 results should not count, add extra code to handle it
}
Unfortunately I don't know python, but I can provide code on F# in case you need it.
EDIT:
Try this:
N, K, M = map(int, input().split())
niz = list(map(int, input().split()))
nizk = list(range(1, K+1))
izlazi = []
def GetMinLengthStartingFromFirstElement(seq):
nizkk = []
for z in range(0, len(seq)):
if seq[z] in nizk:
continue
else:
return -1
for p in range(0, len(seq)):
if seq[p] not in nizkk:
nizkk.append(seq[p])
nizkk.sort()
if nizkk == nizk:
index = p
return index+1
else:
continue
return -1
def GetMinLength(seq):
if len(seq) == 0:
return -1
else:
curMinLength = GetMinLengthStartingFromFirstElement(seq)
if curMinLength == -1:
return -1
minLengthFromRest = GetMinLength(seq[1:])
if minLengthFromRest > -1:
return min(curMinLength,minLengthFromRest)
else:
return curMinLength;
for r in range(0, M):
operacija = list(map(int, input().split()))
index = 0
if operacija[0] == 2:
minLength = GetMinLength(niz)
izlazi.append(minLength)
else:
index, repl = map(int, operacija[1:])
niz[index - 1] = repl
print(izlazi)