Next Word in Lexicographic Order - python

Problem
There is a word given. We need to find the next word occurring in lexicographic order.For example, if word is lkjihfg then the next word would be lkjihgf.
This is a problem at Hackerrank that you can see here.
The problem for reference:
Complete the biggerIsGreater function in the editor below. It should
return the smallest lexicographically higher string possible from the
given string or no answer.
My effort
What i've tried was finding the maximum index(say indx) of word such that after it all the characters are non increasing.Then swapping the char at indx with the minimum char ahead that is greater than char at indx. Finally, reversing the string after indx.
Code
def biggerIsGreater(w):
ww = list(set(w))
indx = -1
l = [ord(ch)for ch in w]
for i in range(len(l)-1):
if l[i] < l[i+1]:
indx = i
else:
continue
if indx == -1:
return "no answer"
j = len(l) - 1
for x in range(j,indx,-1):
if l[x] > l[indx]:
l[x], l[indx] = l[indx], l[x]
break
l[indx+1 : ] = l[len(l) - 1 : indx : -1]
y = []
for z in l:
y.append(chr(z))
ans = ''.join(y)
return ans
The problem with this code is that it's not passing all the test cases because it's producing wrong results.

The good thing about your solution is that it has a good time complexity - O(n), where n is a size of the input string. That's why you don't have any timeout errors.
The problem with this code is, however is not all the test cases are validating.
That's so because you've missed one important case in your loop below:
for x in range(j,indx,-1):
if l[x] >= l[indx]:
l[x], l[indx] = l[indx], l[x]
break
Consider a case like 5 4 6 4. So, your indx is 2 and because of your condition l[x] >= l[indx] you will replace l[2] = 4 with l[0] = 4. This way, your next word in lexicographical order won't change and you'll get a wrong result 5 4 4 6 even though it must be 5 6 4 4.
So, if you make your condition stricter > and not >= because you actually need a LARGER character from the left then the solution will be working correctly.
Hence, just change your condition to > :
for x in range(j,indx,-1):
if l[x] > l[indx]: # fix the sign here
l[x], l[indx] = l[indx], l[x]
break
I tested your code with the fix and it passed all the tests.

Related

traversing through a list using recursion

So I am new to recursion and I am trying to make a program where you can enter a list and python tests each integer (lets say 9 for example) and sees if the integer following it is doubled. So if I entered a list of 2 4 8 16 32, would return 4, and -5 -10 0 6 12 9 36, would return 2 because -5 followed by -10 is one and 6 followed by 12 is the second. This is the code I have so far. I feel like I am very close. but just a few thing stand in my way. Any help would be great!
L = []
def countDouble(L):
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
print(y[1])
print(y[0])
count = 0
y[0] += y[0]
# unsure of how to multiple y[0] by 2
if y[0]*2 == y[1]:
count += 1
else:
count += 0
#how would I traverse through the rest of the entered list using recursion?
print(count)
countDouble(L)
If you want/need to solve it using recursion, the following will do the trick:
def count_sequential_doubles(li, count=0):
return count_sequential_doubles(li[1:], count + int(li[0] * 2 == li[1])) if len(li) > 1 else count
I would suggest this recursive way:
def countDouble(L):
count = 0
if len(L) == 1:
return count
else:
if int(L[0])*2 == int(L[1]):
count += 1
return count + countDouble(L[1:])
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = countDouble(y)
print(count)
I urge you to read the entire answer, but in case you are not interested in tips, notes and the process of finding the solution, here are two solutions:
solution using recursion (not recommended):
x = input()
y = x.split(' ')
count = 0
def countDouble(i):
if(i+1 == len(y)):
return 'recursion ends here when'
if(int(y[i])*2==int(y[i+1])):
count += 1
countDouble(i+1)
countDouble(0)
print(count)
this solution just imitates a while loop:
solution using a while loop (recommended):
x = input()
y = x.split(' ')
count = 0
i = 0
while(i < len(y) - 1):
if(int(y[i]) * 2 == int(y[i+1])):
count += 1
i += 1
print(count)
Before I continue, here are a few tips and notes: (some of them will only make sense after)
I assume the 14 in your example is a typo
I didn't put the code in a function because it's not needed, but you can change it easily.
In your code, you are passing L as a parameter to the countDouble() function, but you don't use it. if you don't need a parameter don't pass it.
when splitting the input, the values of the list are still strings. so you have to invert them to integers (for instance, you can do that with the int() 'function') before comparing their values - otherwise multiplying by 2 will just repeat the string. for example: '13'*2 is the string '1313'
I don't know why you why you added y[0] to itself in line 9, but based on the code that comes after this would yield incorrect results, you don't need to change the elements in order to get their value multiplied by 2.
notice that in the else block, nothing has changed. adding 0 to the count doesn't change it. so you can remove the else block entirely
While it's possible to solve the problem in recursion, there's something else designed for these kind of problems: loops.
The problem is essentially repeating a simple check for every element of a list.
This is how I would arrive to a solution
so we want to run the following 'code':
if(y[0]*2 == y[1]):
count += 1
if(y[1]*2 == y[2]):
count += 1
if(y[2]*2 == y[3]):
count += 1
...
of course the computer doesn't understand what "..." means, but it gives us an idea to the pattern in the code. now we can do the following:
divide the extended 'code' into similar sections.
identify the variables in the pattern - the values that change between sections
find the starting values of all variables
find a pattern in the changes of each variable
find a breaking point, a condition on one of the variables that tells us we have reached the last repeating section.
here are the steps in this specific problem:
the sections are the if statements
the variables are the indexes of the elements in y we compare
the first index starts at 0 and the second at 1
both indexes increase by one after each if-statement
when the second index is bigger then the last index of y then we already checked all the elements and we can stop
so all is left is to set the needed variables, have a while loop with the breaking condition we found, and in the while loop have the general case of the repeating sections and then the changing of the variables.
so:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
# setting the starting values of the variables
index1 = 0
index2 = 1
# creating a loop with the breaking condition
while(index2 < len(y)):
# the general case of the repeated code:
if(int(y[index1]) * 2 == int(y[index2])):
count += 1
# changing the variables for the next loop
index1 += 1
index2 += 1
print(count)
We see that the index2 is just index1 + 1 at all time. so we can replace it like that:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
index1 = 0
while(index1 + 1 < len(y)):
if(int(y[index1]) * 2 == int(y[index1 + 1])):
count += 1
index1 += 1
print(count)
Note: You can use a for loop similarly to the while loop
So in summary, you can use recursion to solve the problem, but the recursion would just be imitating the process of a loop:
in each call, the breaking condition will be checked, the repeated code would run and the variables/parameters would change.
Hope you find this answer useful :)
Final edit: OP edited his example so my other code didnt apply
Some good questions people are asking, but in the spirit of helping, here's a recursive function that returns the count of all doubles.
def get_doubles_count_with_recursion(a_list, count, previous=None):
while a_list:
try:
first = previous if previous else a_list.pop(0)
next_item = a_list.pop(0)
except IndexError:
return count
if next_item / 2 == first:
count += 1
return get_doubles_count_with_recursion(a_list, count, next_item)
return count
a_list = [1, 3, 5, 10, 11, 14, 28, 56, 88, 116, 232, 464, 500]
doubles = get_doubles_count_with_recursion(a_list, 0)
print(doubles == 5)
Probably could clean it up a bit, but it's a lot easier to read than the other guy's ;)
If I'm reading your question right, you want a count of all pairs where the 2nd item is double the first. (and the 14 in the first list is a typo). In which case a simple function like this should do the job:
#a = [2,4,8,16,32]
a = [-5, -10, 0, 16, 32]
count = 0
for i, x in enumerate(a):
# Stop before the list overflows
if i < len(a) - 1:
# If the next element is double the current one, increment the counter
if a[i+1] == x * 2:
count = count + 1
else:
break
print(count)

Palindrome Partitions

Given a string split it into as few strings as possible such that each string is a palindrome
k = "racecarannakayak"
palin = [] # to get palindrome strs
i, j = 0, 2
while j != len(k) + 1:
if k[i:j] == k[:j][::-1]:
palin.append(k[i:j])
i += 1
j += 1
print(palin)
the result is ["racecar"] but it should be ["racecar", "anna", "kayak"]
what is wrong with my code??
There are a few issues with your code.
First, your pointer i keeps iterating even though your point j is
smaller than it in most of the iterations. Meaning you are doing
stuff like k[10:2] which will be pointless.
Second, your pointer j works only once over the whole array. Meaning
you either need multiple passes OR you need to reset the i pointer to
the last position of j pointer IF you find a palindrome.
Third, there is no condition to make sure that single alphabet string
is not considered a palindrome by your inversion logic
Last, there seems to be a typo, as mentioned by #fas in the comments. k[i:j]==k[i:j][::-1] instead of what you have written.
If you consider the above 3 and make the necessary changes you should get the right results to your code as below -
k = "racecarannakayak"
palin = [] # to get palindrome strs
i, j = 0, 2
while j != len(k) + 1:
if k[i:j] == k[i:j][::-1] and len(k[i:j])>2: #Check length is >2
palin.append(k[i:j])
i=j #Reset i pointer to current j
j += 1
print(palin)
['racecar', 'anna', 'kayak']

Efficient permutations of array elements with no repetitions in python [duplicate]

So I received a challenge that states the following:
"Design a program that takes as input a 9 digit number where no digit appears twice and produces as output an arrangement of the same 9 digits corresponding to the next highest number. If no such number exists, the algorithm should indicate this. So for example, if the input is 781623954 the output would be 781624359."
So I came up with this idea to flip the indexes, so check the last index with the one right before to see which is bigger and compare then flip if necessary but for some reason my code isn't working. I only did the work for checking the last two digits not all the digits, so if you can help me out and check it for me and if you have any better ideas on how to tackle this problem, please share.
input = raw_input("Enter 9 Digits: ")
x = 9
while x>0:
x-=1
if input[8] > input[7]:
temp = input[8]
input[8] == input[7]
input[7] == temp
print input
break
Here's a more efficient approach, using the algorithm of the 14th century Indian mathematician Narayana Pandita, which can be found in the Wikipedia article on Permutation. This ancient algorithm is still one of the fastest known ways to generate permutations in order, and it is quite robust, in that it properly handles permutations that contain repeated elements.
The code below includes a simple test() function that generates all permutations of an ordered numeric string.
#! /usr/bin/env python
''' Find the next permutation in lexicographic order after a given permutation
This algorithm, due to Narayana Pandita, is from
https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order
1. Find the largest index j such that a[j] < a[j + 1]. If no such index exists,
the permutation is the last permutation.
2. Find the largest index k greater than j such that a[j] < a[k].
3. Swap the value of a[j] with that of a[k].
4. Reverse the sequence from a[j + 1] up to and including the final element a[n].
Implemented in Python by PM 2Ring 2015.07.28
'''
import sys
def next_perm(a):
''' Advance permutation a to the next one in lexicographic order '''
n = len(a) - 1
#1. Find the largest index j such that a[j] < a[j + 1]
for j in range(n-1, -1, -1):
if a[j] < a[j + 1]:
break
else:
#This must be the last permutation
return False
#2. Find the largest index k greater than j such that a[j] < a[k]
v = a[j]
for k in range(n, j, -1):
if v < a[k]:
break
#3. Swap the value of a[j] with that of a[k].
a[j], a[k] = a[k], a[j]
#4. Reverse the tail of the sequence
a[j+1:] = a[j+1:][::-1]
return True
def test(n):
''' Print all permutations of an ordered numeric string (1-based) '''
a = [str(i) for i in range(1, n+1)]
i = 0
while True:
print('%2d: %s' % (i, ''.join(a)))
i += 1
if not next_perm(a):
break
def main():
s = sys.argv[1] if len(sys.argv) > 1 else '781623954'
a = list(s)
next_perm(a)
print('%s -> %s' % (s, ''.join(a)))
if __name__ == '__main__':
#test(4)
main()
I am not convinced that your approach of flipping digits is guaranteed to find the next highest number (at least not without further checks)
Here a simple solution:
Simply increment the input number and check if the conditions are met or if no number can be found.
set() can be used to get the set of unique digits in the number.
input_num = '781623954'
next_num = int(input_num) + 1
input_digits = set(input_num)
found = False
while not found:
next_num += 1
next_digits = set(str(next_num))
found = len(next_digits) == 9 and input_digits == next_digits
if next_num > 987654321:
break
if found:
print(next_num)
else:
print("No number was found.")
input[8] == input[7]
input[7] == temp
you probably meant:
input[8] = input[7]
input[7] = temp
didn't you?
Which, as stated in the comments, wouldn't work directly on the string, as it is immutable in Python. So, as a first step, you could make a list of characters from that string:
input = list(input)
and as a last step, get a string back from the modified list:
input = ''.join(input)
BTW, you might want to benefit from Python tuple unpacking which allows you to swap two variables without having to introduce a third one:
input[7], input[8] = input[8], input[7]

Alternate letters in a string - code not working

I am trying to make a string alternate between upper and lower case letters. My current code is this:
def skyline (str1):
result = ''
index = 0
for i in str1:
result += str1[index].upper() + str1[index + 1].lower()
index += 2
return result
When I run the above code I get an error saying String index out of range. How can I fix this?
One way using below with join + enumerate:
s = 'asdfghjkl'
''.join(v.upper() if i%2==0 else v.lower() for i, v in enumerate(s))
#'AsDfGhJkL'
This is the way I would rewrite your logic:
from itertools import islice, zip_longest
def skyline(str1):
result = ''
index = 0
for i, j in zip_longest(str1[::2], islice(str1, 1, None, 2), fillvalue=''):
result += i.upper() + j.lower()
return result
res = skyline('hello')
'HeLlO'
Explanation
Use itertools.zip_longest to iterate chunks of your string.
Use itertools.islice to extract every second character without building a separate string.
Now just iterate through your zipped iterable and append as before.
Try for i in range(len(str1)): and substitute index for i in the code. After, you could do
if i % 2 == 0: result += str1[i].upper()
else: result += str1[i].lower()
For every character in your input string, you are incrementing the index by 2. That's why you are going out of bounds.
Try using length of string for that purpose.
you do not check if your index is still in the size of your string.
It would be necessary to add a condition which verifies if the value of i is always smaller than the string and that i% 2 == 0 and that i == 0 to put the 1st character in Upper
with i% 2 == 0 we will apply the upper one letter on two
for i, __ in enumerate(str1):
if i+1 < len(str1) and i % 2 == 0 or i == 0:
result += str1[i].upper() + str1[i + 1].lower()
I tried to modify as minimal as possible in your code, so that you could understand properly. I just added a for loop with step 2 so that you wouldn't end up with index out of range. And for the final character in case of odd length string, I handled separately.
def skyline (str1):
result = ''
length = len(str1)
for index in range(0, length - 1, 2):
result += str1[index].upper() + str1[index + 1].lower()
if length % 2 == 1:
result += str1[length - 1].upper()
return result
You can use the following code:
def myfunc(str1):
result=''
for i in range(0,len(str1)):
if i % 2 == 0:
result += str1[i].upper()
else:
result += str1[i].lower()
return result
in your code you are get 2 word by one time so you should divide your loop by 2 because your loop work by depending your input string so make an variable like peak and equal it to len(your input input) then peak = int(peak/2) it will solve your pr
def func(name):
counter1 = 0
counter2 = 1
string = ''
peak = len(name)
peak = int(peak/2)
for letter in range(1,peak+1):
string += name[counter1].lower() + name[counter2].upper()
counter1 +=2
counter2 +=2
return string

Python nested loops - no output

I've just started learning python. I am trying to check if the integer x is palindrome then divide it by a number between range (starting from largest y i.e. 999 ) y=999,998,...,100. If x/y=z and z is also a 3 digit integer then finish. Otherwise subtract 1 from x and do the same procedure.
def EuQ4():
x=998001
p=999
while 10000 < x:
x=x-1
if str(x)== str(x)[::-1]:
while p>100:
if x%p==0:
Pal=x/p
if Pal < 999:
print (Pal,p)
break
else:
x=x-1
else:
p=p-1
else:
x=x-1
EuQ4()
This is question 4 from Project Euler i.e. Find the largest palindrome made from the product of two 3-digit numbers.
You have a few logic errors in here. Some cause loops that just never end. For example, what happens when x % p == 0 but Pal is larger 999? You would get an infinite loop.
I made a few modifications, but it could still use some work.
def EuQ4():
x = 998001
while 10000 < x:
if str(x) == str(x)[::-1]:
print("{} is a pali!".format(x))
# Move it here so each time it stats at the right
# number or else it will just skip it after it does it once.
p = 999
while p > 100:
if x % p == 0:
pali = int(x / p)
if pali < 999:
print(pali, p)
return
p -= 1
x -= 1
EuQ4()
Edit:
I found these mistakes by using the debugger in my IDE. You could have easily done the same thing by going through the code line by line a few times.
I am sorry but it was hurting my head to read your question. If you are trying to learn Python while attempting these questions then I would propose this alternate answer - it does not answer your question but it does lead to the solution and I think it is more Pythonic. The question asks to find the largest palindrone made from the product of two 3 digit numbers. So the inputs should be 3 digit numbers. This code will allow you to specify the number of digits, max and min (as integers).
I am not proposing that this be the best solution the the Euler Problem posed rather it is a solution that gives you exposure to a range of features in Python.
def min_value(integer):
min_val = '1'
for n in range(0,integer-1):
min_val+='0'
return int(min_val)
def max_value(integer):
max_val = '9'
for n in range(0,integer-1):
max_val += '9'
return int(max_val) +1
def find_max_palindrones(x,y):
minimum_value = min_value(x)
maximum_value = max_value(y)
palindrones = []
working_range = [number for number in range(minimum_value,maximum_value,1)]
for x_value in working_range:
for y_value in working_range:
product = x_value * y_value
orig_order = [item for item in str(product)]
rev_order = [item for item in str(product)[::-1]]
if orig_order == rev_order:
palindrones.append(product)
max_p = max(palindrones)
return max_p
>>>find_max_palindrones(3,3)
906609
Put p=999 befor while p > 100 or use for p in range(999, 100, -1).
p = 999
while p > 100
And I think you call x=x-1 too many times.

Categories