String index out of range in Python - python

def romanToNum(word):
word = word.upper()
numeralList2 = list(zip(
[1000, 500, 100, 50, 10, 5, 1],
['M', 'D', 'C', 'L', 'X', 'V', 'I']
))
num = 0
x = []
a = 0
b = 2
if len(word) % 2 != 0:
word = word + "s"
for i in range(0,len(word)):
x.append(word[a:b])
a = a + 2
b = b + 2
print(x[i])
for n in x:
for nNum,rNum in numeralList2:
if n == rNum:
num = nNum + num
elif n == (n[0] + n[1]):
num = (nNum*2) + num
elif n[0] == rNum:
r1 = 0
r1 = nNum
elif n[1] == rNum:
r2 = 0
r2 = nNum
elif r1 < r2:
num = num + (r2 - r1)
elif r1 > r2:
num = num + (r1 + r2)
return num
romanToNum("xxx")
I am getting the following error:
elif n == (n[0] + n[1]):
IndexError: string index out of range
and it doesn't matter where I put that in the loop, it just wont recognize that n has an index value.
I also get this error: Traceback (most recent call last):
which points to when i call my function: romanToNum("xxx")
I'm not really sure what's going on because I added a print statement to where I'm appending my list and there is an index of at least [0] when I print it all out. Any help here?
I have looked through stack for similar questions but the solution for them is an indentation or because they had a negative index( [-1] ) or something along those lines but all my indentation is correct and my index's are all positive.

Well n is an element of x. The IndexError on the line n == n[0] + n[1] means that a certain n has length less than 2.
You added an word = word + 's' to probably guard against having one character elements in x but it doesn't really work.
If you look at how you build the x list you do:
x = []
a = 0
b = 2
if len(word) % 2 != 0:
word = word + "s"
for i in range(0,len(word)):
x.append(word[a:b])
a = a + 2
b = b + 2
print(x[i])
So in your example you start with x = [] and word = 'XXX'. Then you add an s to obtain word = 'XXXs'.
The loop over i does the following:
i=0 so x.append(word[0:2]); a = a+2; b = b+2 so that x = ['XX'] and a=2 and b=4.
i=1 so x.append(word[2:4]); a = a+2; b = b+2 so that x = ['XX', 'Xs'] and a=4 and b=6.
i=2 so x.append(word[4:6]); a = a+2; b = b+2 so that x = ['XX', 'Xs', ''] and a=6 and b=8.
i=3 so x.append(word[6:8]); a = a+2; b = b+2 so that x = ['XX', 'Xs', '', ''] and a=8 and b=10.
And here you see that n can be the empty string, which means when doing n == n[0] + n[1] you end up with an IndexError.
I believe you wanted to group the characters two by two, but then the i should use a step of 2:
for i in range(0, len(word), 2):
x.append(word[i:i+2])
In this way i is 0, then 2, then 4 etc
By the way: once you have fixed this the condition n == n[0] + n[1] seems pretty odd, because if n is a two character string (as it should be if you fix the code) then the condition will always be true. What are you trying to really do here?

This is the culprit:
for i in range(0,len(word)):
x.append(word[a:b])
a = a + 2
b = b + 2
At the end of this loop, x will be ['XX', 'Xs', '', '']. Since you are grouping the characters in groups of two, the total number of groups will be half the length of the string. So just halve the number of iterations with range(0,len(word)/2) or range(0,len(word),2)

You have a problem with your first for loop that goes farther than expected, affecting an empty string to x[i]. It should probably be : for i in range(int(len(word)/2)):
Then your second loop needs fixing too.
if n == rNum : is never realised since rNum is a one character string and x's length is 2. Try n == rNum+"s".
n == n[0] + n[1] is always True for a string of 2 characters. You must mean n == rNum * 2
Also, the use of x += 1 is recommended instead of x = x + 1.

Related

Fast replacing characters in a string and checking if a substring is a palindrome

A line is received at the input to the program, the length of which is not more than 10^5.
After this comes a lot of instructions of two types:
set l r c (l - start of substring, r - end of substring, c - character to replace all substring characters)
ask l r (l - start of substring, r - end of substring)
1 ⩽ l ⩽ r ⩽ string length
Set command, replaces all substring characters with the given character. And the ask command must check if the substring is a palindrome.
For example, if the program receives such data at the input
5 5 - line length and number of commands
abcde
ask 1 5
set 2 4 z
ask 2 4
set 5 5 a
ask 5 5
Then in response she will print:
NO
YES
YES
My implementation is very not optimal. It works, but for too long. Tell me, please, a faster algorithm.
def p(s):
for i in range(len(s)//2):
if s[i]!=s[-1-i]:
return False
return True
N,Q = [int(i) for i in input().split()]
s = list(input())
for i in range(Q):
c = input().split()
if c[0]=='set':
s[int(c[1])-1:int(c[2])] = [c[3]]*(int(c[2])-int(c[1])+1)
elif c[0]=='ask':
if p(s[int(c[1])-1:int(c[2])]):
print('YES')
else:
print('NO')
implementation with a separate search for all palindromes, after replacing some substring.
def allPalindromeSubstring(s):
v = {}
pivot = 0.0
while pivot < len(s):
palindromeRadius = pivot - int(pivot)
while ((pivot + palindromeRadius) < len(s) and
(pivot - palindromeRadius) >= 0 and
(s[int(pivot - palindromeRadius)] ==
s[int(pivot + palindromeRadius)])):
l = int(pivot - palindromeRadius) + 1
r = int(pivot + palindromeRadius + 1)
if l in v:
v[l].append(r)
else:
v[l] = [r]
palindromeRadius += 1
pivot += 0.5
return v
N,Q = [int(i) for i in input().split()]
s = list(input())
ps = allPalindromeSubstring(s)
for i in range(Q):
c = input().split()
l = int(c[1]); r = int(c[2])
if c[0]=='set':
s[l-1:r] = [c[3]]*(r-l+1)
ps = allPalindromeSubstring(s)
elif c[0]=='ask':
b = True
if l in ps:
for j in ps[l]:
if r<=j:
print('YES')
b = False
break
if b:
print('NO')
I don't have enough data to time my new code. But this must be faster than yours :)
import numpy as np
from math import ceil
N, Q = [int(i) for i in input().split()]
s = np.asarray(list(input()), dtype="unicode")
for i in range(Q):
c = input().split()
if c[0] == 'set':
s[int(c[1]) - 1:int(c[2])] = c[3]
elif c[0] == 'ask':
st, ed = int(c[1]) - 1, int(c[2]) - 1
if np.array_equal(s[st:st+ceil((ed-st)/2)+1], s[ed:st+ceil((ed-st)/2)-1:-1]):
print('YES')
else:
print('NO')

How to check if any item exists in the list

How to check whether a list and an element of a list with such an index exist in the list itself?
I have a list [[10,10,9], [10,10,10], [10,10,10]]
Then I enter the number of coordinates (k) and the coordinates themselves. At these coordinates, I have to subtract 8 from the cell and 4 with each cell standing next to it. But what if there are no cells nearby?
When checking if field [r + f] [c + s] in field: it always gives a negative answer. How to make a check?
for i in range(k):
for j in range(1):
f = drops[i][j]
s = drops[i][j + 1]
field[f][s] -= 8
for r in range(-1, 1):
for c in range(-1, 1):
if not (r == c == 1):
if field[r + f][c + s] in field:
field[r + f][c + s] -= 4
You just have to check whether the index isn't at the start or the end of the list.
n = 2
mylist = [4, 5, 8, 9, 12]
if len(mylist) > n+1:
mylist[n+1] -= 1
if n > 0:
mylist[n-1] -= 1
Slice assignment might help. You have to avoid letting an index go negative, but something like
s = slice(max(n-1,0), n+2)
x[s] = [v-1 for v in x[s]]
isn't too repetitive, while handling the edge cases n == 0 and n == len(s) - 1. (It won't work ifn` is explicitly set to a negative index, though.)

XOR greater than 4 of even-even or odd odd pair

Given an array of even and odd numbers, I want to get the number of (even-even) and (odd-odd) pairs whose XOR is greater than or equal to 4. I tried this with the code below but it runs in O(n^2), (yikes). Please can anyone suggest a means of optimization?
n = int(raw_input()) #array size
ar = map(int, raw_input().split()) #the array
cnt = 0
for i in xrange(len(ar)):
for j in xrange(i+1, len(ar)):
if ar[i] ^ ar[j] >= 4 and (not ar[i] & 1 and not ar[j] & 1):
cnt += 1; #print ar[i],ar[j],ar[i]^ar[j];
elif ar[i] ^ ar[j] >= 4 and (ar[i] & 1 and ar[j] & 1):
cnt += 1
print cnt
EDIT: I discovered something. any number x, which gives a remainder after % 4, i.e x % 4 != 0, will result to 2 when XORed to a number -2 itself. For example, 6. It is not divisible by 4, therefore, 6 XOR 6-2 (4),==> 2. 10 is not divisible by 4, hence, 10 XOR 10-2 (8) ==> 2. Can you please tell me how this could help me optimize my code? I just know now that I will just look for numbers divisible by 4 and find the count of their + 2.
For simplicity, let´s assume the array does not have duplicates. For the XOR between 2 numbers to be >= 4, they need to have any different bit (excluding lower 2 bits). Given that we already know they are even-even or odd-odd pairs, their lowest bit is the same.
Note that for any number X, X XOR (X + 4 + k) will always be >= 4. So the problem is considering what happens with X XOR (X + 1), X XOR (X + 2) and X XOR (X + 3).
X XOR (X + 1) will be >= 4 when the third lowest bit has changed by adding only 1. That means, we had X ending in 011 so X + 1 ends in 100 or we had X ending in 111 so X + 1 ends in 000. In both cases, this means X % 4 = 3. In any other case (X % 4 != 3), X XOR (X + 1) will be < 4.
For X XOR (X + 2) to be >= 4, the third lowest bit has changed by adding 2. This means, X ended in 011, 010, 111, or 110. So we now have X % 4 = 3 or X % 4 = 2.
For X Xor (X + 3) to be >= 4, the third lowest bit has changed by adding 3. This means, X ended in 011, 010, 001, 111, 110, 101. So we now have X % 4 = 3, X % 4 = 2 or X % 4 = 1.
Here is pseudocode:
for each element in array:
count[element] += 1
total += 1
for each X in sorted keys of count:
if X % 4 == 3:
answer += count[X + 1] + count[X + 2] + count[X + 3]
if X % 4 == 2:
answer += count[X + 2] + count[X + 3]
if X % 4 == 1:
answer += count[X + 3]
total -= count[X]
answer += total - (count[X + 1] + count[X + 2] + count[X + 3]) # all X + 4 + K work
To account for duplicates, we need to avoid counting a number against itself. You will need to keep the count of each number, and do the same as the above with the modification that the answer will be the count of that number * (all the others - the amount of X + 2 numebers)
You should work on separating your code, one improvement is the use of set to avoid repeating operations, although it may get more memory overhead.
import random
from operator import xor
import itertools
random.seed(10)
in_values = [random.randint(0, 10) for _ in range(100)]
def get_pairs_by(condition, values):
odds = set(filter(lambda x: x % 2 == 0, values))
evens = set(filter(lambda x: x % 2 == 1, values))
def filter_pairs_by_condition(values):
return (
(x, y) for x, y in set(
map(lambda x: tuple(sorted(x)),
itertools.product(iter(values), iter(values))))
if condition(xor(x, y))
)
return list(
itertools.chain.from_iterable(
(filter_pairs_by_condition(x) for x in (odds, evens))
)
)
print(get_pairs_by(lambda x: x >= 4, in_values))
The keypoint is:
set(map(lambda x: tuple(sorted(x)),
itertools.product(iter(values), iter(values)))))
What we are doing is that pairs of (5, 7) and (7, 5) should be evaluated as being the same, so we take rid of them there.
Here you have the live example
EDIT:
As a quick update to your code, you can use a dictionary to memoize previously computed pairs, hence:
n = int(raw_input()) #array size
ar = map(int, raw_input().split()) #the array
cnt = 0
prev_computed = {}
for i in xrange(len(ar)):
for j in xrange(i+1, len(ar)):
if any(x in prev_compued for x in ((ar[i], ar[j]), (ar[j], ar[i]))):
cnt += 1
continue
if ar[i] ^ ar[j] >= 4 and (not ar[i] & 1 and not ar[j] & 1):
cnt += 1; #print ar[i],ar[j],ar[i]^ar[j];
prev_computed[(ar[i], ar[j])] = True
prev_computed[(ar[j], ar[i])] = True
elif ar[i] ^ ar[j] >= 4 and (ar[i] & 1 and ar[j] & 1):
cnt += 1
prev_computed[(ar[i], ar[j])] = True
prev_computed[(ar[j], ar[i])] = True
print cnt
def xor_sum(lst)
even_dict = a dictionary with keys being all even numbers of lst and values being their frequencies
odd_dict = a dictionary with keys being all odd numbers of lst and values being their frequencies
total_even_freq = sum of all frequencies of even numbers
total_odd_freq = sum of all frequencies of odd numbers
even_res = process(even_dict, total_even_freq)
odd_res = process(odd_dict, total_odd_freq)
return even_res + odd_res
def process(dict, total_freq)
res = 0
for num in dict.keys
# LSB of XOR of 2 even numbers is always 0
# Let p = XOR of 2 even numbers; if p < 4 then p = 00000000 (minus_2) or 00000010 (plus_2)
plus_2 = num+2
minus_2 = num-2
count = 0
if( (plus_2 XOR num) < 4 and (plus_2 is a key of dict) )
count = count + frequency_of_plus_2
if( (minus_2 XOR num) < 4 and (minus_2 is a key of dict) )
count = count + frequency_of_minus_2
count = count + num
res = res + (total_freq+1-count)
return res
Complexity:
Assuming you have a good hash function for your dictionaries (a hashmap), the average time complexity is O(n)

Find the index in the string where count(symbols) in left half=count(symbols) in right half?

Given a string, say s="##$$$#", how can I find the index where the number of "#" symbols before the index is equal to the number of "$" symbols after the index?
Example: if s="##$$$#", then the output would be 2.
Explanation: before index 2 we have 2 "#" symbols and after index 2 we have 2 "$" symbols
I tried finding the mid index first and count the symbols(# and $) on both the sides,if they are equal then print the mid index else increment the mid and proceed the same way..
But I'm not able to get the logic correctly.
An approach is :
At any given index 'i' ,
Count_Pound[i] = Count_Pound[i-1] + 1 if charAt(i-1) is '#'
= Count_Pound[i-1] if charAt(i-1) is '$'
E.g. Count_Pound[0] = 0;
Count_Pound[1] = 0 + 1 if charAt(0) = '#'
= 0 if charAt(0) is '$'
Similar approach holds when you traverse in the reverse direction.
Count_Dollar[j] = Count_Dollar[j+1] + 1 if charAt(j+1) is '$'
= Count_Dollar[j+1] if charAt(j+1) is '#'
E.g. Count_Dollar[str.length -1] = 0
Count_Dollar[str.length - 2] = 0 + 1 if charAt(str.length-1) is '$'
= 0 if charAt(str.length-1) is '#'
Once you have these two arrays after traversing forward and reverse ways - BTW you could have these two arrays built in just one loop, have two indices one incrementing and one decrementing. Then go through these arrays and get the largest ( I am assuming you want the largest ).
for i : 0 to str.length-1:
if Count_Pound[i] == Count_Dollar[i] && max_value < Count_Pound[i]
max_value = Count_Pound[i]
ans = i
return ans
Space complexity is not bad : O(n), time complexity is good : O(n)
Could you try using a for loop to test each place in the string? From what I can gather, it seems when you said index 2 is the answer, the index itself is excluded so you get str[:index] to str[index+1:]
So you'd get something like this:
def half(str, first_half, second_half):
for i in range(len(str)):
if str[:i].count(first_half) == str[i+1:].count(second_half):
return i
return None
This one should scale as O(N):
def same_count(s):
c1 = 0
c2 = 0
l1 = list()
l2 = list()
n = len(s)
for i in range(n):
# count forward from start
if s[i] == '#':
c1 += 1
# count backward from end
if s[n - i - 1] == '$':
c2 += 1
l1.append(c1)
l2.append(c2)
# reverse second list
l2 = list(reversed(l2))
# find index where counts are the same
match = [i for i in range(1,n-1) if
l1[i-1] == l2[i+1]]
if match:
return match[0]
else:
return None
print(same_count('##$$$#'))
print(same_count('###$$$##'))
print(same_count('#$$'))
#output:
#2
#None
#1
string = '##$$$#'
foundHash = False
foundDollar = False
dollarIndex = -1
hashCount = 0
dollarCount = 0
for i,c in enumerate(string):
if c == '#' and not foundHash:
hashCount = hashCount + 1
foundHash = True
elif c == '#' and foundHash and not foundDollar:
hashCount = hashCount + 1
elif c == '$' and foundHash and not foundDollar:
dollarIndex = i
foundDollar = True
elif c == '$' and foundHash and foundDollar:
dollarCount = dollarCount + 1
else:
if hashCount == dollarCount:
break
else:
foundHash = False
foundDollar = False
dollarIndex = -1
hashCount = 0
dollarCount = 0
if c == '#' and not foundHash:
hashCount = hashCount + 1
foundHash = True
if dollarIndex > 0 and hashCount == dollarCount:
print("index = %d" %(dollarIndex))
else:
print("index not found")

Collatz conjecture sequence

The Collatz conjecture
what i am trying to do:
Write a function called collatz_sequence that takes a starting integer and returns the sequence of integers, including the starting point, for that number. Return the sequence in the form of a list. Create your function so that if the user inputs any integer less than 1, it returns the empty list [].
background on collatz conjecture:
Take any natural number n. If n is even, divide it by 2 to get n / 2, if n is odd multiply it by 3 and add 1 to obtain 3n + 1. Repeat the process indefinitely. The conjecture is that no matter what number you start with, you will always eventually reach 1.
What I have so far:
def collatz_sequence(x):
seq = [x]
if x < 1:
return []
while x > 1:
if x % 2 == 0:
x= x/2
else:
x= 3*x+1
return seq
When I run this with a number less than 1 i get the empty set which is right. But when i run it with a number above 1 I only get that number i.e. collatz_sequence(6) returns [6]. I need this to return the whole sequence of numbers so 6 should return 6,3,10,5,16,8,4,2,1 in a list.
You forgot to append the x values to the seq list:
def collatz_sequence(x):
seq = [x]
if x < 1:
return []
while x > 1:
if x % 2 == 0:
x = x / 2
else:
x = 3 * x + 1
seq.append(x) # Added line
return seq
Verification:
~/tmp$ python collatz.py
[6, 3, 10, 5, 16, 8, 4, 2, 1]
def collatz_sequence(x):
seq = [x]
while seq[-1] > 1:
if x % 2 == 0:
seq.append(x/2)
else:
seq.append(3*x+1)
x = seq[-1]
return seq
Here's some code that produces what you're looking for. The check for 1 is built into while statement, and it iteratively appends to the list seq.
>>> collatz_sequence(6)
[6, 3, 10, 5, 16, 8, 4, 2, 1]
Note, this is going to be very slow for large lists of numbers. A cache won't solve the speed issue, and you won't be able to use this in a brute-force solution of the project euler problem, it will take forever (as it does every calculation, every single iteration.)
Here's another way of doing it:
while True:
x=int(input('ENTER NO.:'))
print ('----------------')
while x>0:
if x%2==0:
x = x/2
elif x>1:
x = 3*x + 1
else:
break
print (x)
This will ask the user for a number again and again to be put in it until he quits
def collatz(x):
while x !=1:
print(int(x))
if x%2 == 0:
x = x/2
else:
x = 3*x+1
this is what i propose..
seq = []
x = (int(input("Add number:")))
if (x != 1):
print ("Number can't be 1")
while x > 1:
if x % 2 == 0:
x=x/2
else:
x = 3 * x + 1
seq.append (x)
print seq
This gives all the steps of a single number. It has worked with a 50-digit number in 0,3 second.
collatz = []
def collatz_sequence(x):
while x != 1:
if x % 2 == 0:
x /= 2
else:
x = (3*x + 1)/2
collatz.append(int(x))
print(collatz)
collatz_sequence()
Recursion:
def collatz(n):
if n == 1: return [n]
elif n % 2 == 0: return [n] + collatz(int(n/2))
else: return [n] + collatz(n*3+1)
print(collatz(27))
steps=0
c0 = int(input("enter the value of c0="))
while c0>1:
if c0 % 2 ==0 :
c0 = c0/2
print(int(c0))
steps +=1
else:
c0 = (3 * c0) + 1
print(int(c0))
steps +=1
print("steps= ", steps)
import numpy as np
from matplotlib.pyplot import step, xlim, ylim, show
def collatz_sequence(N):
seq = [N]
m = 0
maxN = 0
while seq[-1] > 1:
if N % 2 == 0:
k = N//2
seq.append(N//2)
if k > maxN:
maxN = k
else:
k = 3*N+1
seq.append(3*N+1)
if k > maxN:
maxN = k
N = seq[-1]
m = m + 1
print(seq)
x = np.arange(0, m+1)
y = np.array(seq)
xlim(0, m+1)
ylim(0, maxN*1.1)
step(x, y)
show()
def collatz_exec():
print('Enter an Integer')
N = int(input())
collatz_sequence(N)
This is how you can use it:
>>> from collatz_sequence import *
>>> collatz_exec()
Enter an Integer
21
[21, 64, 32, 16, 8, 4, 2, 1]
And a plot that shows the sequence:
seq = []
def collatz_sequence(x):
global seq
seq.append(x)
if x == 1:
return
if (x % 2) == 0:
collatz_sequence(x / 2)
else:
collatz_sequence((x * 3) + 1)
collatz_sequence(217)
print seq
def collataz(number):
while number > 1:
if number % 2 == 0 :
number = number //2
print(number)
elif number % 2 ==1 :
number = 3 * number + 1
print(number)
if number == 1 :
break
print('enter any number...!')
number=int(input())
collataz(number)

Categories