I'm working through an 'easy' HackerRank exercise, and I'm trying to apply multiple conditions to items in a list. The 'solution' I attempted appears consistent with others discussed on the site, but does not work. I am interested in learning why it fails, because I cannot discern my error.
The purpose of the function is to count the valleys along a person's walk. The relative elevations of the walk are stored in a string (ie. 'DDUU' for down, down, up, up). The walk begins and finishes at sea level. The function accepts the number of steps 'n' and the route string. In the case above, it should return v = 1, and in fact, it does. However, when I run this on longer cases, the function's returns are incorrect.
def countingValleys(n, s):
valleys = 0
position = 0
positions = []
for step in s:
if step == 'U':
position += 1
else:
position -= 1
positions.append(position)
for position in positions:
if positions[position] < 0 and positions[position+1] == 0:
valleys += 1
return valleys
s = 'DDUUUUDDDDUUDDUU'
v = countingValleys(16, s)
In this case, len(s) = 16 and the number of valleys is 3, but the number returned is 4. I'm trying to understand the error. Examples of new code are appreciated, but I mostly want to know what is wrong within the code above and how that might be adjusted. I am hoping to better understand multiple conditions, not just fix the code.
Thanks in advance!
instead of :
for position in positions:
if positions[position] < 0 and positions[position+1] == 0:
valleys += 1
you need:
for i in range(n):
if positions[i] < 0 and positions[i+1] == 0:
valleys += 1
The following lines of code are erroneous:
for position in positions:
if positions[position] < 0 and positions[position+1] == 0:
And I suppose you wanted to have something like:
for i in range(len(positions)-1):
if positions[position] < 0 and positions[position+1] == 0:
For example: If you have the following
s = 'DDDUUU', then your positions array looks like this: [-1, -2, -3, -2, -1, 0] and you will check position[-2] two times: (position[-2] is the 2nd value of positions read from the right and is -1.)
Both times your if will evaluated to true and thus you get two values and this is surely not intended.
Related
I am trying to implement a solution to the 'n-parenthesis problem'
def gen_paren_pairs(n):
def gen_pairs(left_count, right_count, build_str, build_list=[]):
print(f'left count is:{left_count}, right count is:{right_count}, build string is:{build_str}')
if left_count == 0 and right_count == 0:
build_list.append(build_str)
print(build_list)
return build_list
if left_count > 0:
build_str += "("
gen_pairs(left_count - 1, right_count, build_str, build_list)
if left_count < right_count:
build_str += ")"
#print(f'left count is:{left_count}, right count is:{right_count}, build string is:{build_str}')
gen_pairs(left_count, right_count - 1, build_str, build_list)
in_str = ""
gen_pairs(n,n,in_str)
gen_paren_pairs(2)
It almost works but isn't quite there.
The code is supposed to generate a list of correctly nested brackets whose count matches the input 'n'
Here is the final contents of a list. Note that the last string starts with an unwanted left bracket.
['(())', '(()()']
Please advise.
Here's a less convoluted approach:
memory = {0:[""]}
def gp(n):
if n not in memory:
local_mem = []
for a in range(n):
part1s = list(gp(a))
for p2 in gp(n-1-a):
for p1 in part1s:
pat = "("+p1+")"+p2
local_mem.append(pat)
memory[n] = local_mem
return memory[n]
The idea is to take one pair of parentheses, go over all the ways to divide the remaining N-1 pairs between going inside that pair and going after it, find the set of patterns for each of those sizes, and make all of the combinations.
To eliminate redundant computation, we save the values returned for each input n, so if asked for the same n again, we can just look it up.
In a for loop, what's the best way to check a previous and next in a list without getting an index out of bounds? (See code below)
Let's say I'm writing a function to check if every color is next to another of the same color, such as:
def colorChecker(colorList):
colors = True
for i in range(len(colorList)): #No editing this line
if not ((i > 0 and colorList[i] == colorList[i-1]) or ((i < len(colorList)-1) and colorList[i] == colorList[i+1])):
colors = False
return colors
This gives me an index out of bounds exception...
I'm attempting to check for the index out of bounds and bounce it from the if statement before it goes into checking colorList[len(colorlist)+1]...but obviously this isn't the correct way to do it. How would you better write this?
It is better to iterate over three set of elements like this, using zip
for prev,cur,nxt in zip(colorList, colorList[1:], colorList[2:]):
if (prev == cur) or (cur == nxt):
# Do something
Since you're checking previous and next elements, you only need to start from the second element and end at the next to last element (you can also break after colors = False, since one invalid triple is enough):
for i in range(1, len(colorList) - 1):
if not(colorList[i] == colorList[i-1] or colorList[i] == colorList[i+1]):
# ... etc
If you need to check the first and last elements as well, use this:
for i in range(len(colorList)):
if not((i > 0 and colorList[i] == colorList[i-1]) or (i < len(colorList) - 1 and colorList[i] == colorList[i+1])):
colors = False
break
#MrGeek is entirely correct, and #Sunitha's technique is even more elegant -- I suggest you use one of their solutions since they avoid unnecessary logic.
But the answer to why are you getting 'index out of bounds' is because your second check is i < len(colorList) when it should be i < len(colorList) - 1.
Arrays being 0-indexed, the valid indice range for an array of size n is [0 ... n-1].
EDIT: Taking a step back, those errors happen because your if-condition is a bit complicated. I would advise breaking it down to smaller pieces -- keeping your loop unchanged, I would write:
def colorChecker(colorList):
colors = True
for i in range(len(colorList)):
prevI = i - 1;
nextI = i + 1;
isDifferentFromPrev = prevI < 0 or colorList[i] != colorList[prevI]
isDifferentFromNext = nextI >= len(colorList) or colorList[i] != colorList[nextI]
if isDifferentFromPrev and isDifferentFromNext:
colors = False
return colors
Note I made a few "not (a or b)" to "not a and not b" substitutions to simplify.
Reading the above code, you immediately understand what is being done: setting colors to False if any color in the array is different from the colors surrounding it.
I'm still extremely new to python, and currently stuck on this problem. Basically, I take a list of numbers and add them to each other, starting at zero. The code writes out each line into a new array. If in this new array I find two of the same numbers, it stops and returns that number. The original list of values repeats itself if no duplicate is found.
Here's what I have so far:
file = open("list.txt", "r")
array1 = file.readlines()
total = 0
finalValue = 0
for i in range(0,len(array1)):
array1[i] = int(array1[i])
array2 = []
i = 0
counter = 0
while finalValue == 0:
total += array1[i]
array2.append(total)
print(array2)
for c in range(0,len(array2)):
if (total == array2[c]):
counter += 1
if counter == 2:
finalValue = total
break
if (i == len(array1)-1):
i = 0
else:
i += 1
counter = 0
print(finalValue)
I think the counter is working, but it never finds a duplicate, i.e. it never hits the second counter.
There are plenty of ways to make your code simpler in Python, but first of all, your problem is that the condition total == array2[c] compares elements of the array with your total, not with each other. For example, if your array is [1,3,3], the second 3 would be compared to 4, not to 3.
If I understand your code, I think you want to change total == array2[c] to array1[i] == array2[c] - but that's just an immediate fix, you can use python's list techniques to make this code much simpler.
UPDATE 1 (Oct.16): The original code had a few logic errors which were rectified. The updated code below should now produce the correct output for all lists L, S.T they meet the criteria for a special list.
I am trying to decrease the running time of the following function:
The "firstrepeat" function takes in a special list L and an index, and produces the smallest index such that L[i] == L[j]. In other words, whatever the element at L[i] is, the "firstrepeat" function returns the index of the first occurrence of this element in the list.
What is special about the list L?:
The list may contain repeated elements on the increasing side of the list, or the decreasing side, but not both. i.e [3,2,1,1,1,5,6] is fine but not [4,3,2,2,1,2,3]
The list is decreasing(or staying the same) and then increasing(or staying the same).
Examples:
L = [4,2,0,1,3]
L = [3,3,3,1,0,7,8,9,9]
L = [4,3,3,1,1,1]
L = [1,1,1,1]
Example Output:
Say we have L = [4,3,3,1,1,1]
firstrepeat(L,2) would output 1
firstrepeat(L,5) would output 3
I have the following code. I believe the complexity is O(log n) or better (though I could be missing something). I am looking for ways to improve the time complexity.
def firstrepeat(L, i):
left = 0
right = i
doubling = 1
#A Doubling Search
#In doubling search, we start at one index and then we look at one step
#forward, then two steps forward, then four steps, then 8, then 16, etc.
#Once we have gone too far, we do a binary search on the subset of the list
#between where we started and where we went to far.
while True:
if (right - doubling) < 0:
left = 0
break
if L[i] != L[right - doubling]:
left = right - doubling
break
if L[i] == L[right - doubling]:
right = right - doubling
doubling = doubling * 2
#A generic Binary search
while right - left > 1:
median = (left + right) // 2
if L[i] != L[median]:
left = median
else:
right = median
f L[left] == L[right]:
return left
else:
return right
I have a solution for this problem on codewars.com that works when I run it in Sublime, but when I try to submit, I get this error:
Process was terminated. It took longer than 12000ms to complete
Why did my code time out?
Our servers are configured to only allow a certain amount of time for your code to execute. In rare cases the server may be taking on too much work and simply wasn't able to run your code efficiently enough. Most of the time though this issue is caused by inefficient algorithms. If you see this error multiple times you should try to optimize your code further.
The goal of the function is to find the next biggest number after a given number that you can make by rearranging the digits of a given number. For example, if I was given 216, I would need to return 261.
This is the code I have now:
import itertools
def next_bigger(n):
# takes a number like 472 and puts it in a list like so: [4, 7, 2]
num_arr = [int(x) for x in str(n)]
perms = []
total = ''
# x would be a permutation of num_arr, like [7, 2, 4]
for x in itertools.permutations(num_arr):
for y in x:
total += str(y)
perms.append(int(total))
total = ''
# bigger is all permutations that are bigger than n,
# so bigger[0] is the next biggest number.
# if there are no bigger permutations, the function returns -1
bigger = sorted([x for x in perms if x > n])
return bigger[0] if bigger else -1
I'm new to coding in Python, so is there some mistake I am making which causes my code to be extremely inefficient? Any suggestions are welcome.
Thanks for all the help you guys gave me. I ended up finding a solution from here using the Next Lexicographical Permutation Algorithm
This is my tidied up version of the solution provided here:
def next_bigger(n):
# https://www.nayuki.io/res/next-lexicographical-permutation-algorithm/nextperm.py
# https://www.nayuki.io/page/next-lexicographical-permutation-algorithm
# Find non-increasing suffix
arr = [int(x) for x in str(n)]
i = len(arr) - 1
while i > 0 and arr[i - 1] >= arr[i]:
i -= 1
if i <= 0:
return -1
# Find successor to pivot
j = len(arr) - 1
while arr[j] <= arr[i - 1]:
j -= 1
arr[i - 1], arr[j] = arr[j], arr[i - 1]
# Reverse suffix
arr[i : ] = arr[len(arr) - 1 : i - 1 : -1]
return int(''.join(str(x) for x in arr))
Why are you getting TLE (time limit exceeded)?
Because your algorithm has wrong complexity. How much permutations you will find for list with 3 elements? Only 6. But what if we use list with 23 elements? 25852016738884976640000.
This is too much for time limit.
So, if you want to have solve this problem you have to find solution without permutations. Please rethink how the numbers are written. The number 271 is bigger then 216 because the number on the second position has bigger value 7>1.
So, your solution has to find two numbers and swap them position. The number on the left have to smaller then the second one.
For example - for 111115444474444 you should find 5 and 7.
Then you swap them - and now you should sort sublist on right from the first position.
For example after swapped the values (111117444454444) you have to sort (444454444) -> (444444445). Now merge all, and you have solution.
import functools
def next_bigger(a):
a = map(int, str(a))
tmp = list(reversed(a))
for i, item_a in enumerate(reversed(a)):
for j in (range(i)):
if item_a < tmp[j]:
#you find index of number to swap
tmp[i]=tmp[j]
print(list(reversed(tmp[i:])))
tmp[j]=item_a
fin = list(reversed(tmp[i:])) + sorted(tmp[:i])
return functools.reduce(lambda x,y: x*10+y, fin)
return -1
A simple backtracking approach is to consider the digits one at a time. Starting from the most significant digit, pick the smallest number you have left that doesn't prevent the new number from exceeding the input. This will always start by reproducing the input, then will have to backtrack to the next-to-last digit (because there aren't any other choices for the last digit). For inputs like 897654321, the backtracking will immediately cascade to the beginning because there are no larger digits left to try in any of the intermediate slots.
You should sorting the num_arr in desc order and creating a number by combining the result.
Since OP required, next largest, OP needs to check starting from right, which right digit is larger then its very left digit and rotate their position.
Here is the final code:
def next_bigger(n):
num_arr = [int(x) for x in str(n)]
i = 0
i = len(num_arr) - 1
while(i > 0):
if num_arr[i] > num_arr[i-1]:
a = num_arr[i]
num_arr[i] = num_arr[i-1]
num_arr[i-1] = a
break
else:
i = i-1
newbig = "".join(str(e) for e in num_arr)
return int(newbig)
Now I edit to calculate next bigger element.
def perms(s):
if(len(s)==1):
return [s]
result=[]
for i,v in enumerate(s):
result += [v+p for p in perms(s[:i]+s[i+1:])]
return result
a=input()
b=perms(str(a))
if len(b)!=1:
for i in range(0,len(b)):
if b[i]==a:
print (b[i+1])
break
else:
print ("-1")