Time-Complexity of the problem in CodeForces contest #771 - python

I'm currently trying to upsolve one problem from CodeForces and I face so many time issues. I've tried a variety of modifications, however my code still doesn't get submitted.
Any suggestions on how I can improve the time complexity of my code?
The main idea of the problem can be found here: https://codeforces.com/contest/1638/problem/C
A description of the problem is as follows:
You are given a permutation p_1,p_2,…,p_n. Then, an undirected graph is constructed in the following way: add an edge between vertices i, j such that i < j if and only if p_i > p_j. Your task is to count the number of connected components in this graph.
Two vertices u and v belong to the same connected component if and only if there is at least one path along edges connecting u and v.
A permutation is an array consisting of n distinct integers from 1 to n in arbitrary order. For example, [2,3,1,5,4] is a permutation, but [1,2,2] is not a permutation (2 appears twice in the array) and [1,3,4] is also not a permutation (n=3 but there is 4 in the array).
Input
Each test contains multiple test cases. The first line contains a single integer t (1 ≤ t ≤ 10**5) — the number of test cases. Description of the test cases follows.
The first line of each test case contains a single integer n (1 ≤ n ≤ 10**5) — the length of the permutation.
The second line of each test case contains n integers p_1,p_2,…,p_n (1≤p_i≤n) — the elements of the permutation.
It is guaranteed that the sum of n over all test cases does not exceed 2⋅10**5.
Output
For each test case, print one integer k — the number of connected components.
import sys
input = sys.stdin.readline
for case in range(int(input())):
length = int(input())
line = list(map(int, input().split()))
quantity = 0
for i in range(1, length):
if line[i] > line[i-1] and max(line[:i]) < min(line[i:]):
quantity += 1
print(1 if quantity >= length else quantity + 1)
Thanks for your help!

This implements my min/max caching scheme. I have not submitted this, so I don't actually know if it will meet the timing. This is O(3N) instead of O(N*N), so it will take slightly longer for short lists, but SHOULD be better for long lists.
You don't need to do input = sys.stdin.readline; that's effectively what the input function already is. Also, the if clause in your final line confuses me. Your final for can never go more than length-1 iterations, so quantity can never be >= length.
import sys
for case in range(int(input())):
length = int(input())
line = list(map(int, input().split()))
maxx = 0
maxs = []
for i in line:
if i > maxx:
maxx = i
maxs.append(maxx)
minx = 999999
mins = []
for i in line[::-1]:
if i < minx:
minx = i
mins.insert(0,minx)
quantity = 0
for i in range(1, length):
if line[i] > line[i-1] and maxs[i-1] < mins[i]:
quantity += 1
print(quantity + 1)
You can replace the final section of the code with a comprehension, although I think that's a micro-optimization:
quantity = sum(
line[i] > line[i-1] and maxs[i-1] < mins[i]
for i in range(1, length)
)
print(quantity + 1)

Related

Getting all subsets from subset sum problem on Python using Dynamic Programming

I am trying to extract all subsets from a list of elements which add up to a certain value.
Example -
List = [1,3,4,5,6]
Sum - 9
Output Expected = [[3,6],[5,4]]
Have tried different approaches and getting the expected output but on a huge list of elements it is taking a significant amount of time.
Can this be optimized using Dynamic Programming or any other technique.
Approach-1
def subset(array, num):
result = []
def find(arr, num, path=()):
if not arr:
return
if arr[0] == num:
result.append(path + (arr[0],))
else:
find(arr[1:], num - arr[0], path + (arr[0],))
find(arr[1:], num, path)
find(array, num)
return result
numbers = [2, 2, 1, 12, 15, 2, 3]
x = 7
subset(numbers,x)
Approach-2
def isSubsetSum(arr, subset, N, subsetSize, subsetSum, index , sum):
global flag
if (subsetSum == sum):
flag = 1
for i in range(0, subsetSize):
print(subset[i], end = " ")
print("")
else:
for i in range(index, N):
subset[subsetSize] = arr[i]
isSubsetSum(arr, subset, N, subsetSize + 1,
subsetSum + arr[i], i + 1, sum)
If you want to output all subsets you can't do better than a sluggish O(2^n) complexity, because in the worst case that will be the size of your output and time complexity is lower-bounded by output size (this is a known NP-Complete problem). But, if rather than returning a list of all subsets, you just want to return a boolean value indicating whether achieving the target sum is possible, or just one subset summing to target (if it exists), you can use dynamic programming for a pseudo-polynomial O(nK) time solution, where n is the number of elements and K is the target integer.
The DP approach involves filling in an (n+1) x (K+1) table, with the sub-problems corresponding to the entries of the table being:
DP[i][k] = subset(A[i:], k) for 0 <= i <= n, 0 <= k <= K
That is, subset(A[i:], k) asks, 'Can I sum to (little) k using the suffix of A starting at index i?' Once you fill in the whole table, the answer to the overall problem, subset(A[0:], K) will be at DP[0][K]
The base cases are for i=n: they indicate that you can't sum to anything except for 0 if you're working with the empty suffix of your array
subset(A[n:], k>0) = False, subset(A[n:], k=0) = True
The recursive cases to fill in the table are:
subset(A[i:], k) = subset(A[i+1:, k) OR (A[i] <= k AND subset(A[i+i:], k-A[i]))
This simply relates the idea that you can use the current array suffix to sum to k either by skipping over the first element of that suffix and using the answer you already had in the previous row (when that first element wasn't in your array suffix), or by using A[i] in your sum and checking if you could make the reduced sum k-A[i] in the previous row. Of course, you can only use the new element if it doesn't itself exceed your target sum.
ex: subset(A[i:] = [3,4,1,6], k = 8)
would check: could I already sum to 8 with the previous suffix (A[i+1:] = [4,1,6])? No. Or, could I use the 3 which is now available to me to sum to 8? That is, could I sum to k = 8 - 3 = 5 with [4,1,6]? Yes. Because at least one of the conditions was true, I set DP[i][8] = True
Because all the base cases are for i=n, and the recurrence relation for subset(A[i:], k) relies on the answers to the smaller sub-problems subset(A[i+i:],...), you start at the bottom of the table, where i = n, fill out every k value from 0 to K for each row, and work your way up to row i = 0, ensuring you have the answers to the smaller sub-problems when you need them.
def subsetSum(A: list[int], K: int) -> bool:
N = len(A)
DP = [[None] * (K+1) for x in range(N+1)]
DP[N] = [True if x == 0 else False for x in range(K+1)]
for i in range(N-1, -1, -1):
Ai = A[i]
DP[i] = [DP[i+1][k] or (Ai <=k and DP[i+1][k-Ai]) for k in range(0, K+1)]
# print result
print(f"A = {A}, K = {K}")
print('Ai,k:', *range(0,K+1), sep='\t')
for (i, row) in enumerate(DP): print(A[i] if i < N else None, *row, sep='\t')
print(f"DP[0][K] = {DP[0][K]}")
return DP[0][K]
subsetSum([1,4,3,5,6], 9)
If you want to return an actual possible subset alongside the bool indicating whether or not it's possible to make one, then for every True flag in your DP you should also store the k index for the previous row that got you there (it will either be the current k index or k-A[i], depending on which table lookup returned True, which will indicate whether or not A[i] was used). Then you walk backwards from DP[0][K] after the table is filled to get a subset. This makes the code messier but it's definitely do-able. You can't get all subsets this way though (at least not without increasing your time complexity again) because the DP table compresses information.
Here is the optimized solution to the problem with a complexity of O(n^2).
def get_subsets(data: list, target: int):
# initialize final result which is a list of all subsets summing up to target
subsets = []
# records the difference between the target value and a group of numbers
differences = {}
for number in data:
prospects = []
# iterate through every record in differences
for diff in differences:
# the number complements a record in differences, i.e. a desired subset is found
if number - diff == 0:
new_subset = [number] + differences[diff]
new_subset.sort()
if new_subset not in subsets:
subsets.append(new_subset)
# the number fell short to reach the target; add to prospect instead
elif number - diff < 0:
prospects.append((number, diff))
# update the differences record
for prospect in prospects:
new_diff = target - sum(differences[prospect[1]]) - prospect[0]
differences[new_diff] = differences[prospect[1]] + [prospect[0]]
differences[target - number] = [number]
return subsets

Google Kickstart 2020 Round C - Countdown. Unable to understand why my solution is incorrect

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.

Longest Arithmetic Progression

Given a list of numbers arr (not sorted) , find the Longest Arithmetic Progression in it.
Arrays: Integer a
1 ≤ arr.size() ≤ 10^3. and
-10^9 ≤ arr[i] ≤ 10^9.
Examples:
arr = [7,6,1,9,7,9,5,6,1,1,4,0] -------------- output = [7,6,5,4]
arr = [4,4,6,7,8,13,45,67] -------------- output = [4,6,8]
from itertools import combinations
def arithmeticProgression2(a):
n=len(a)
diff = ((y-x, x) for x, y in combinations(a, 2))
dic=[]
for d, n in diff:
k = []
seq=a
while n in seq:
k.append(n)
i=seq.index(n)
seq=seq[i+1:]
n += d
dic.append(k)
maxx=max([len(k) for k in dic])
for x in dic:
if len(x)==maxx:
return x
in case arr.size() is big enough. my code will be run more than 4000ms.
Example :
arr = [randint(-10**9,10**9) for i in range(10**3)]
runtime > 4000ms
How to reduce the space complexity for the above solution?
One of the things that makes the code slow is that you build series from scratch for each pair, which is not necessary:
you don't actually need to build k each time. If you just keep the step, the length and the start (or end) value of a progression, you know enough. Only build the progression explicitly for the final result
by doing this for each pair, you also create series where the start point is in fact in the middle of a longer series (having the same step), and so you partly do double work, and work that is not useful, as in that case the progression that starts earlier will evidently be longer than the currently analysed one.
It makes your code run in O(n³) time instead of the possible O(n²).
The following seems to return the result much faster in O(n²), using dynamic programming:
def longestprogression(data):
if len(data) < 3:
return data
maxlen = 0 # length of longest progression so far
endvalue = None # last value of longest progression
beststep = None # step of longest progression
# progressions ending in index i, keyed by their step size,
# with the progression length as value
dp = [{} for _ in range(len(data))]
# iterate all possible ending pairs of progressions
for j in range(1, len(arr)):
for i in range(j):
step = arr[j] - arr[i]
if step in dp[i]:
curlen = dp[i][step] + 1
else:
curlen = 2
dp[j][step] = curlen
if curlen > maxlen:
maxlen = curlen
endvalue = arr[j]
beststep = step
# rebuild the longest progression from the values we maintained
return list(reversed(range(endvalue, endvalue - maxlen * beststep, -beststep)))

Similarity Measure in Python

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

Interviewstreet's Insertion sort program

I tried to program Interiewstreet's Insertion sort challenge Link for the challenge
in Python and here is my code shown below.
The program runs fine for a limit(which I'm not sure of) of input elements, but returns a false output for inputs of larger sizes. Can anyone guide me what am I doing wrong?
# This program tries to identify number of times swapping is done to sort the input array
"""
=>Get input values and print them
=>Get number of test cases and get inputs for those test cases
=>Complete Insertion sort routine
=>Add a variable to count the swapping's
"""
def sort_swap_times(nums):
""" This function takes a list of elements and then returns the number of times
swapping was necessary to complete the sorting
"""
times_swapped = 0L
# perform the insertion sort routine
for j in range(1, len(nums)):
key = nums[j]
i = j - 1
while i >= 0 and nums[i] > key:
# perform swap and update the tracker
nums[i + 1] = nums[i]
times_swapped += 1
i = i - 1
# place the key value in the position identified
nums[i + 1] = key
return times_swapped
# get the upper limit.
limit = int(raw_input())
swap_count = []
# get the length and elements.
for i in range(limit):
length = int(raw_input())
elements_str = raw_input() # returns a list of strings
# convert the given elements from str to int
elements_int = map(int, elements_str.split())
# pass integer elements list to perform the sorting
# get the number of times swapping was needed and append the return value to swap_count list
swap_count.append(sort_swap_times(elements_int))
# print the swap counts for each input array
for x in swap_count:
print x
Your algorithm is correct, but this is a naive approach to the problem and will give you a Time Limit Exceed signal on large test cases (i.e., len(nums) > 10000). Let's analyze the run-time complexity of your algorithm.
for j in range(1, len(nums)):
key = nums[j]
i = j - 1
while i >= 0 and nums[i] > key:
# perform swap and update the tracker
nums[i + 1] = nums[i]
times_swapped += 1
i = i - 1
# place the key value in the position identified
nums[i + 1] = key
The number of steps required in the above snippet is proportional to 1 + 2 + .. + len(nums)-1, or len(nums)*(len(nums)-1)/2 steps, which is O(len(nums)^2).
Hint:
Use the fact that all values will be within [1,10^6]. What you are really doing here is finding the number of inversions in the list, i.e. find all pairs of i < j s.t. nums[i] > nums[j]. Think of a data structure that allows you to find the number of swaps needed for each insert operation in logarithmic time complexity. Of course, there are other approaches.
Spoiler:
Binary Indexed Trees

Categories