Simple Python program to convert roman numerals to the international system - python-3.x

I've tried to put together a basic program to convert roman numbers to conventional numbers and I seem to have made some mistake and right now it's a hit or miss.Do take a look at the code.I've described it better in the code as comments.Thanks!
def roman(roman_num):
inter_dic = {
"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000
}
output = 0
num = roman_num.upper()
#this is to ensure that the number appears as uppercase alphabets
for numeral in num:
numeral_index = num.index(numeral)
#I've squeezed an exception here to handle the last digit in the number
try:
#for the additive part
if inter_dic[numeral] > inter_dic[num[numeral_index + 1]]:
output += inter_dic[numeral]
#for the subtraction part
elif inter_dic[numeral] < inter_dic[num[numeral_index + 1]]:
output -= inter_dic[numeral]
elif inter_dic[numeral] == inter_dic[num[numeral_index + 1]]:
output += inter_dic[numeral]
#the following line is actually dead code,but I've added it just in case.
else:
print("There was an error.")
#the IndexError will be called when the last digit is reached and so the last digit
#will be added
except IndexError:
output += inter_dic[numeral]
return output
assert roman("cxcix") == 199
#this returns an assertion error
#when the function is called,the output is 179

This should do what you want:
def roman(roman_num):
inter_dic = {
"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000
}
x = list(map(lambda x: inter_dic[x], roman_num.upper()))
for idx in range(len(x)-1):
if x[idx+1] > x[idx]:
x[idx] *= -1
return x
decimal = roman("cxcix")
print(decimal) # Output: [100, -10, 100, -1, 10]
print(sum(decimal)) # Output: 199
This works on the assumption that the numbers are properly structured. As in, the numbers represented should be in order of the biggest to the smallest.
The above code will work even if you just give it one character, because the loop is based on the range(len() - 1) of the given list that is created when we translate the letters into their integers.
If x == [100] then len(x)-1 == 0 which will just terminate the for-loop immediately, so we will not encounter any IndexError inside of the loop.
To explain what is happening in your code that differs from my version we can create a simple example here:
lst = list("abcabc")
for idx, letter in enumerate(lst):
print(f"Letter: {letter}, list.index: {lst.index(letter)}, Actual index: {idx}")
Output:
Letter: a, list.index: 0, Actual index: 0
Letter: b, list.index: 1, Actual index: 1
Letter: c, list.index: 2, Actual index: 2
Letter: a, list.index: 0, Actual index: 3
Letter: b, list.index: 1, Actual index: 4
Letter: c, list.index: 2, Actual index: 5
If we look at the documentation for list.index, we can see this description:
index(self, value, start=0, stop=2147483647, /)
Return first index of value.
Raises ValueError if the value is not present.
So because there are repeated values inside of lst when we call to check for it's index, it just returns the first value that matches the variable we give it.

Related

Moving 0's in a list without interfering the order of other non-zero items

I am trying to solve a programming quiz on Leet Code. The question is:
Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements.
Note that you must do this in-place without making a copy of the array.
 
Example 1:
Input: nums = [0,1,0,3,12]
Output: [1,3,12,0,0]
Example 2:
Input: nums = [0]
Output: [0]
 
Constraints:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
 
My solution
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
count = 0
for item in nums:
if item == 0:
nums.remove(item)
count += 1
for i in range(0,count):
nums.append(0)
What my solution is trying to do is that:
It traverses the entire list.
If any item is 0, the item is removed. The counter increments by 1.
After traversing the list, append counter number of 0's to the list.
However, my solution didn't pass the following test case.
Test input:
[0,0,1]
Expected output:
[1,0,0]
My output:
[0,1,0]
Where does my script go wrong?
You can try this in a simple way:
>>> nums = [0,1,0,3,12]
>>> output=[i for i in nums if i!=0]
>>> for cnt in range(nums.count(0)):
output.extend("0")
>>> print(output)
[1, 3, 12, '0', '0']
Same code can be convert into one liner solution as suggested by #deadshot:
[i for i in nums if i!=0] + [0] * nums.count(0)
The other answer together with the one line solution from comment are nice pythonic solutions. If you really need the same list modified in place you can do something similar with bubble-sort, swap elements until all the zeroes are at the end.
nums = [0, 1, 0, 3, 12]
done = False
while not done:
done = True
for i in range(len(nums) - 1):
if nums[i] == 0 and nums[i + 1] != 0:
nums[i] = nums[i + 1]
nums[i + 1] = 0
done = False
print(nums)
In your solution, remove removes only the first occurrence or a zero. To make it work you need to remove until there are no zeros remaining and then append the final number of zeroes at the end.

Finding Longest Substring Without Repeating Characters returns 0 for a string

I am trying to find the Longest Substring Without Repeating Characters. For Example
Input: s = "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
My code:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
l=[]
c=0
for i in s:
if (i not in l):
l.append(i)
else:
if(len(l)>c):
c=len(l)
l=[]
l.append(i)
return c
My question is when I pass this string (" "), it is returing 0 instead of 1. Why is this coming as 0?
How about if you set your default value of c to 1, i.e. c=1
Edit-01:
add an additional if condition:
if (len(s) == 0):
return 0

Function that fills in missing numbers to create complete sequence

I'm trying to create a function that will fill in any missing numbers in between two numbers in a list. The original list must be altered and cannot be new.
For example: [13,15,20] would return [13,14,15,16,17,18,19,20].
Note that I am not allowed to use a range function.
Here's my code:
def complete(list1):
i= 0
if len(list1) > 1:
for number in list1:
if number - list1[i+1] != -1:
number += 1
list1.insert(i + 1, number)
i += 1
return list1
else:
return list1
I got a "list index out of range" error.
Here is the source of your error:
...
for number in list1:
if number - list1[i+1] != -1:
...
i += 1
Basically, there comes a point (that point being the last number in list1) when i+1 gets you out of bounds and you are not doing anything to prevent that from happening. Indexing is tricky like that, so I would like to offer an indexing-free (well, almost) approach. By the way, from your comment to Bonfire's answer, I see that the task is to change original lists in-place. While mutating arguments is considered a very poor coding practice these days, here is a relatively efficient way of doing that:
import typing as t
def complete_sequence(partial: t.List[int]) -> t.List[int]:
# edge case
if len(partial) < 2:
return partial
# a lookup table for numbers we already have
observed = set(partial)
# append numbers we don't have
start = partial[0]
stop = partial[-1]
num = start + 1
while num < stop:
if not num in observed:
partial.append(num)
num += 1
# in-place sort
partial.sort()
return partial
As you see, instead of inserting values between existing numbers (paying O(n) time for each insertion), we can simply append everything (O(1) per insertion) and sort. This not only simplifies the logic (we no longer have to track those pesky indices), but also reduces computational time-complexity from O(n^2) to O(n*log(n)).
To achieve what you want to do I have made some changes to the logic:
def complete(list1):
if len(list1) < 2 : return list1
num = list1[0]
i = -1
while num < list1[-1]:
num += 1
i += 1
if num in list1: continue
if i < len(list1) - 1:
list1.insert(i + 1, num)
else:
list1.append(num)
return list1
print(complete([13, 14, 20]))
# [13, 14, 15, 16, 17, 18, 19, 20]
print(complete([13, 14, 15]))
# [13, 14, 15]

Index going out of range in bisect_left in Python 3

I'm writing this piece of code, in which I've used bisect_left function from the bisect module which is a first-party module of Python. I'm using it with two parameters only i.e. sorted_list and target(the one for which I have to find the suitable index value).
The issue is: If my target is greater than the sum of lowest value and highest value, the function is returning the index = len(sorted_li), due to which I'm getting index error. I can use try and except but more than that I'm curious to know why it is behaving like so.
Following is my code:
from bisect import bisect_left
li = [10,15,3,6,10]
k = 19
def binary_search(sorted_list,target):
index = bisect_left(sorted_list,target)
print(index)
if sorted_list[index] == target:
return index
else:
return False
def function(sorted_li,k):
"""
Given a list of numbers and a number k, return whether any two numbers from the list add up to k.
For example, given [10, 15, 3, 7] and k of 17, return true since 10 + 7 is 17.
"""
print(sorted_li)
for i in range(len(sorted_li)):
print('Next iteration')
print(sorted_li[i])
target = k - sorted_li[i]
j = binary_search(sorted_li,target)
if j:
if j != i:
print(sorted_li[i])
print(sorted_li[j])
return True
else:
if j + 1 < len(sorted_li):
if sorted_li[j+1] == target:
print(sorted_li[i])
print(sorted_li[j+1])
return True
if j - 1 > 0:
if sorted_li[j-1] == target:
print(sorted_li[i])
print(sorted_li[j-1])
return True
return False
if __name__ == "__main__":
li.sort()
a = function(li,k)
print(a)
It's output is as follows:
but when I'm changing k to 18, the code is working fine, the output is as follows:
I've tried with various sets of numbers for the same. The output remains the same.
You're using bisect_left which has next purpose: it looking for the insertion point for x (which is target in your case) in a to maintain sorted order.
So for your case when you call first binary_search first time for 16 (19 - 3), it compare your number with items in li list using binary algorithm and then it returns position for insert 5, because in your list [3, 6, 10, 10, 15] insertion point should be after 15 which is correct.
If you open documentation you can find next method in searching sorted list
which does exactly you need, it searching for the exact item in list and return position of it if it exists either it raises ValueError because item not found.
def index(a, x):
'Locate the leftmost value exactly equal to x'
i = bisect_left(a, x)
if i != len(a) and a[i] == x:
return i
raise ValueError

How do I compare two lists and return a list with elements that share the same index?

I need to take a user input of 4 digits, compare it to a randomly generated 4 digit number, and then return the values they have in common in the same indices.
So for example:
Secret number: 4667
User input: 3608
Output: _ 6 _ _
Unfortunately, when I run what I've written already, it prints the digits in whatever position they're in on the secret number regardless of their position in the user input, and then it also returns a number of "_" for each iteration in the for loop.
Here's what I have so far:
from random import randint
def randomNumber():
numberList = []
digits = 0
while digits != 4:
numberList += [randint(0,9)]
digits += 1
return numberList
secretNumber = randomNumber()
guessNumber = list(input("Enter a four digit number: "))
guessNumber = list(map(int, guessNumber))
guessList = guessNumber.copy()
correctNumbers = []
numberPosition = []
numberPosition.clear()
for number in guessNumber:
if number in secretNumber:
correctNumbers.append(number)
for n in secretNumber:
if n == number:
numberPosition.append(n)
else:
numberPosition.append("_")
I apologize if there's any spacing/tabbing issues with this code block, I copied it over from a Jupyter notebook.
Thanks!
I think the iteration logic is somewhat incorrect. Considering the example -
In [10]: secretNumber = [4,6,6,7]
In [11]: guessNumber = [3,6,0,8]
In [12]: for i in range(len(secretNumber)):
...: if secretNumber[i] == guessNumber[i]:
...: numberPositions.append(secretNumber[i])
...: else:
...: numberPositions.append("_")
...:
In [13]: numberPositions
Out[13]: ['_', 6, '_', '_']
You basically need to iterate only once for the length of either lists (4 in this case) and update the numberPositions variable.
Here's a oneliner of the above code if you are interested -
In [12]: "".join([str(secretNumber[i]) if secretNumber[i] == guessNumber[i] else "_" for i in range(len(secretNumber))])
Out[12]: '_6__'
try this:
def f(a, b):
for i in range(min(len(a), len(b))):
if a[i] == b[i]:
yield i, a[i]
else:
yield i, None
a, b = '123456', '2335678'
print(''.join(['_' if c is None else c for _, c in f(a, b)]))
This one-line code will do it:
In [1]: ''.join(a if a==b else '_' for a, b in zip(secretNumber,guessNumber))
Out[1]: '_6__'

Resources