index out of range even after initialization of var serving as index - python

I dont understand why this is out of range if I initialize j.
This expansion iterates all preceding characters of a slash, and when combined with a space, it is multiplied.
ie
5</ --> 5<5<
5<// --> 5<5<5<
5</ / --> 5<5< 5<5<
Also, is this the best way to accomplish my task?
def ttExpand( program ) :
"""
expand the string manipulation symbols in program into
a TinyTurtle language program.
program -- a TinyTurtle string, possibly with string manipulation symbols
Returns -- a TinyTurtle string after expansion
"""
new_program = ''
array = []
i = 0
j = 0
for ch in program: #while program.index(ch) != len(program) - 1:
if ch == '/':
array.append(program.index(ch))
i += 1
if len(array) == 0:
return program
while j <= len(array):
new_program += (program[:array[j]])
j += 1
return new_program

This doesn't produce the correct result, but it does do what you're trying to do:
#while j <= len(array):
for i in array:
new_program += program[:i] #(program[:array[j]])
It would appear that this approach actually accomplishes what you want:
def tt_expand(program):
'''
expand string manip symbols
Example:
>>> tt_expand('5</')
'5<5<'
>>> tt_expand('5<//')
'5<5<5<'
>>> tt_expand('5</ /')
'5<5< 5<5<'
'''
seen = ''
new_program = ''
prev_token = None
for i, token in enumerate(program):
if token == '/':
if prev_token == ' ':
new_program += new_program.rstrip()
else:
new_program += seen
else:
new_program += token
seen += token
prev_token = token
return new_program
if __name__ == '__main__':
import doctest
doctest.testmod()

The immediate cause is using while j <= len(array): then indexing array with j; sequences have indices starting at 0 and ending len(array) - 1 (usually described as being from 0 (inclusive) to len(array) (exclusive)).
The simple fix that preserves the majority of your code is to change to while j < len(array) so you stop at the last available index in array. That said, you're coding like you've just come from C, indexing instead of iterating.
If you ever find yourself with a loop that is structured like:
i = 0
while i < len(someseq):
item = someseq[i] # or equivalent, where you only use the value retrieved by indexing, not the index itself
what you really want is:
for item in someseq:
In rare cases, you might also need the index (when assigning back to the original sequence) in which case you'd do:
for i, item in enumerate(someseq):
Either of those is markedly faster and simpler than reinventing C-style for loops (rechecking the length on each pass and indexing adds a surprising amount of overhead compared to iterating directly).

You have to do:
while j <= len(array) - 1

Related

Number of the possible arrays that can be formed from a string of digits ( Leetcode 1416. Restore The Array )

Given the leetcode question 1416. Restore The Array:
A program was supposed to print an array of integers. The program
forgot to print whitespaces and the array is printed as a string of
digits s and all we know is that all integers in the array were in the
range [1, k] and there are no leading zeros in the array.
Given the string s and the integer k, return the number of the
possible arrays that can be printed as s using the mentioned program.
I'm trying to solve this question using backtracking, but I can't understand why my solution returns an incorrect output.
class Solution:
def numberOfArrays(self, s: str, k: int) -> int:
ans = 0
def backtrack(s, index, buffer):
nonlocal ans, k
# If we have reached the end of the string,
# we have found a valid array of integers.
if index == len(s):
ans += 1
return
# If the current digit is '0', we cannot form
# a valid number using any of the following digits.
if s[index] == "0":
return
# Try forming a number using the current digit and
# the following digits. If the number is valid,
# continue the backtracking process with the remaining
# digits in the string.
for i in range(index, len(s)):
buffer *= 10
buffer += int(s[i])
if buffer <= k:
backtrack(s, i + 1, buffer)
else:
# If the number is not valid, stop the backtracking
# process and undo any changes made to the buffer.
buffer //= 10
backtrack(s, 0, 0)
return ans
It is able to pass the following test cases:
Input: s = "1000", k = 10000
Output: 1
Input: s = "1000", k = 10
Output: 0
Input: s = "1317", k = 2000
Output: 8
But not this one:
Input: s = "2020", k = 30
Output: 0
Expected: 1
I don't understand why it can't detect the partition [20, 20].
The for-loop in your backtracking algorithm is incorrect.
if buffer <= k, then there is a choice to either
carry the buffer forward (as backtrack(s, i + 1, buffer)), OR
see buffer as an element in the result array. In this case you need to reset the buffer and calls backtrack again.
because of this, the algo failed to detect the [20, 20] case
if buffer > k, you should break out of the for-loop instead of continuing it, because there is a chance that in the next iteration buffer may become <= k and calls backtrack (and you really don't want that). You can try the test case s = "91", k = 3 for example.
Here is a version that works. The main problem with my original code, besides what has already been mentioned by #nicku123, is that we should have a counter and a buffer that is set to zero on every call to the backtracking function.
class Solution:
def numberOfArrays(self, s: str, k: int) -> int:
cache = {}
def backtrack(pos: int) -> int:
original_pos = pos
count = 0
buffer = 0
if pos in cache:
return cache[pos]
for i in range(pos, len(s)):
buffer *= 10
buffer += int(s[i])
if buffer <= k and i+1 <= len(s)-1 and s[i+1] != '0':
count += (backtrack(i+1) % 1000000007)
if buffer > k:
break
else:
if buffer <= k:
count += 1
cache[original_pos] = count
return count
return backtrack(0) % 1000000007

How to check elements in a list WITHOUT using for loops?

Apologies if the title of the question is phrased badly. I am currently trying to make a function that takes in a list of integers from 1 to n, where n is the length of the list. The function should return the first value that is repeated in the list. Duplicates are NOT always next to one another. If one or more integers is less than 1 or if it is not a list, the function should return -1. If there are no duplicates, return 0.
This is my current code:
def find_duplicates(ls):
if type(ls) != list:
return -1
non_dupe = []
i = 0
while i < len(ls):
if ls[i] < 1:
return -1
break
if ls.count(i) > 1:
return i
break
else:
non_dupe.append(i)
i += 1
if len(non_dupe) == len(ls):
return 0
While this code works for a majority of test cases, it doesn't seem to pass
print(find_duplicates([1, 2, 2, 0]))
as it returns 2 instead of the expected -1. I am relatively new to Python and I can't seem to be able to fix this error. I've tried searching for ways to counter this problem but I am not allowed to use for loops to check through a list. Any help is greatly appreciated.
EDIT: I am not allowed to use any of the following but anything else is accepted.
for loops
min() / max()
enumerate() / zip ()
sort()
negative indexing e.g ls[-1]
list slicing
Your code returns a duplicate prematurely; traversing the list, the function first finds 2 as a duplicate, return it, and halts the function immediately. But it has not seen the 0 at the end.
So, you need to let the function see the list all the way towards the end, looking for a negative number. If a negative number is found along the way, you can halt the function. If it does not see a negative number until the end, then let it return the duplicate value:
def find_duplicates(ls):
if not isinstance(ls, list): # check whether ls is a list
return -1
dup = 0
seen = [] # list of numbers seen so far
i = 0 # index
while i < len(ls):
if ls[i] < 1: # if a negative number is found, return -1
return -1
if ls[i] in seen and dup == 0:
dup = ls[i]
seen.append(ls[i])
i += 1
return dup
print(find_duplicates([1, 2, 2, 0])) # -1
print(find_duplicates([1, 1, 2, 2, 3])) # 1
Problem is beacause you are breaking while loop when find a duplicated. In that case, function is finding first the duplicated.
Try this:
def find_duplicates(ls):
if type(ls) is not list:
return -1
duplicated = 0
i = 0
while i < len(ls):
if ls[i] < 1:
return -1
if ls.count(ls[i]) > 1 and duplicated == 0
duplicated = ls[i]
i += 1
return duplicated
Your test case returns 2 because 2 stay at lower indexes comparing to 0.
I would suggest to sort the list before moving on:
def find_duplicates(ls):
if type(ls) != list:
return -1
sorted_list = ls.sorted() #Assign sorted `ls` to another variable, while keeping the order of `ls` intact
non_dupe = []
i = 0
while i < len(ls):
if ls[i] < 1:
return -1
break
if ls.count(i) > 1:
return i
break
else:
non_dupe.append(i)
i += 1
if len(non_dupe) == len(ls):
return 0
Another method I would recommend is using set - a built-in data type of Python. Maybe you should consider trying this approach later on when all test cases are passed. Have a look at this Tutorial for set usage: https://www.w3schools.com/python/python_sets.asp.
You were very close. Try this:
def find_duplicates(ls):
if type(ls) != list:
return -1
non_dupe = []
i = 0
while i < len(ls):
if ls[i] < 1:
return -1
elif ls[i] in non_dupe:
return ls[i]
else:
non_dupe.append(i)
i += 1
return 0
my_list = [1,2,2,0]
result = list(set(filter(lambda x: my_list.count(x) > 1 , my_list)))
# Result => [2]
I hope this solves your problem

Combination of all possible numbers following digit rules in python

Looking for a "pythonish" and simple way to achieve the generation of all possible numbers following different ranges of digits.
Example: Generate strings (representing number) with digits '01234567' ranging from [0-4] occurrences and '8' ranging from [0-10] occurrences and '9' from [0-100]
Example of numbers generated: {'11337899999999', '33567899999999', '245678999999999999999',...}
(all generated numbers should be with sequential digits... so '88119' isn't valid)
So far I came up with a very simple and clean code but that doesn't do exactly what I need:
from itertools import combinations_with_replacement
length = 50
for x in combinations_with_replacement('0123456789', length):
print("".join(x), end=', ')
This will generate the numbers I need but as well a bunch of unnecessary ones, which will delay significantly the execution.
Thought on generating the digits one by one according to the rules and concatenating... but believe this will be a "dirty" solution and as well inefficient.
Anyone knows a nice clean way of doing this? (Itertools, or any other library is welcome)
I am not sure I fully undersand you problem, but i think i have a solution :
import random
def generate(string = ""):
for i in range(9):
i += 1
if i <= 7:
occurrence = 4
elif i == 8:
occurrence = 10
else:
occurrence = 100
string = string + str(i)* random.randint(0, occurrence)
return string
a = generate()
print(a)
if you want it to be lenght 50 use:
a = a[:50]
So, I ended up picking the itertools.combinations_with_replacement and alter it to accept an array containing maximum usage of digits...
Pretty simple, but it's working.
If anyone has any hints on how to look at it differently or adapt anything to speed up, I will be happy with it.
def combinations_with_replacement2(iterable, m_digits, r):
pool = tuple(iterable)
n = len(pool)
if not n and r: return
count = [0] * n
indices = [0] * r
d = 0
for i in range(r):
if count[d] == m_digits[d]: d += 1
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
if indices[i] != n - 1: break
else: return
count = [0] * n
d = indices[i] + 1
for f in range(i, r):
indices[f] = d
count[d] += 1
if count[d] == m_digits[d]: d += 1
yield tuple(pool[i] for i in indices)

Partition Array

Given an array nums of integers and an int k, partition the array (i.e move the elements in nums) such that: All elements < k are moved to the left. All elements >= k are moved to the right
Return the partitioning index, i.e the first index i nums[i] >= k.
class Solution:
def partitionArray(self, nums, k):
# write your code here
if nums == []:
return 0
left = 0
i = 0
while i <= len(nums):
if nums[i] < k:
i += 1
left += 1
else:
r = nums[i]
del nums[i]
nums.append(r)
i += 1
return left
My idea is to going through the list one by one. The num[i] whose larger than k will be removed and append at the end of the num, the one whose smaller than k will be kept at the original place. Once the whole list has been going through, all the smaller num are at the front. left is a counter at this point for return. But I cannot fix the problem with nums[i]. After the each mods to the list, the counter i cannot point at the correct item in the list.
How can I write the code base on this idea???
You're looking for the index(k). This seems like a homework assignment so you may be limited to what built in functionality you can use. However, a pythonic approach to this is
def solution(nums, k):
return sorted(nums).index(k)
You are doing several things I would recommend avoiding.
Concurrent modification; you should not add or delete from a list while looping it.
You can not loop up to i == len(nums) because list indexes start at 0.
Since you are really just looking for index(k) you need only keep track of numbers less than k and not concern yourself with re-organizing the list.
class Solution:
def partitionArray(self,nums, k):
# write your code here
if nums == []:
return 0
left = 0
i = 0
while i < len(nums):
if nums[i] < k:
left += 1
i += 1
return left

How can I loop through a string and print certain items?

lst = 'AB[CD]EF[GH]'
Output: ['A','B','CD','E','F','GH']
This is what I've tried but it's not working...
while(index < len(my_string)):
curr_char = my_string[index]
if(curr_char == '['):
while(curr_char != ']'):
multi = my_string[index + 1]
index += 1
lst += multi
Can anybody please help? Without importing Regex or whatever. I wanna do this without using it.
The problems with the original code seemed to be:
1) lst, index and multi are not initialised
2) the loop is infinite because the loop variable (index) isn't incremented on each iteration.
3) the close bracket needs to be skipped when detected to avoid including it in the final list
This code is an example of how to fix those issues:
def getList(s):
outList=[]
lIndex=0
while lIndex < len(s):
if s[lIndex] == "[":
letters=""
lIndex+=1
while s[lIndex] != "]":
letters+=s[lIndex]
lIndex+=1
outList.append(letters)
else:
outList.append(s[lIndex])
lIndex+=1
return outList
print(getList('AB[CD]EF[GH]'))
You can't use
lst += multi
because you can't concatenate a string with a list.
Moreover, your code enters an infinite loop, because you aren't updating the curr_char variable inside the inner loop, so the condition will always be True.
Also, you are not handling the case when curr_char != '['. And more errors there are.
You can use this code which fixes the above errors while using the same basic logic as your code:
index = 0
multi = ""
res = []
my_str = 'AB[CD]EF[GH]'
while (index < len(my_str)):
curr_char = my_str[index]
if curr_char == '[':
multi += curr_char
while curr_char != ']':
index += 1
curr_char = my_str[index]
multi += curr_char
res.append(multi)
multi = ""
else:
res.append(curr_char)
index += 1
print(res)
Output:
['A', 'B', '[CD]', 'E', 'F', '[GH]']
Please try the following code snippet.
my_string = 'AB[CD]EF[GH]'
lst = []
ind = 0
n = len(my_string)
while (ind < n):
if my_string[ind] == '[':
# if '[' is found, look for the next ']' but ind should not exceed n.
# Your code does not do a ind < n check. It may enter an infinite loop.
ind += 1 # this is done to skip the '[' in result list
temp = '' # create a temporary string to store chars inside '[]'
while ind < n and my_string[ind] != ']':
temp = temp + my_string[ind]
ind+=1
lst.append(temp) # add this temp string to list
ind += 1 # do this to skip the ending ']'.
else:
# If its not '[', simply append char to list.
lst.append(my_string[ind])
ind += 1
print(lst)

Categories

Resources