input: ['baNaNa', 7] # string and step size
required output : 'utGtGt' # every character of string shifted backwards by step size
import ast
in_string = input()
lis = ast.literal_eval(in_string)
st = lis[0]
step = lis[1]
alphabets = 'abcdefghijklmnopqrstuvwxyz'
password = ''
for letter in st:
if letter in alphabets:
index_val = alphabets.index(letter) - (step)
password += alphabets[index_val]
print(password)
Output i am getting is 'utgtgt'. I want 'utGtGt'. Help on this would be appreciated a lot.
The string module has methods to create a transformation dictionary and a translate method to do exactly what you want:
st = "baNaNa"
step = 7
alphabets = 'abcdefghijklmnopqrstuvwxyz'
alph2 = alphabets.upper()
# lower case translation table
t = str.maketrans(alphabets, alphabets[-step:]+alphabets[:-step])
# upper case translation table
t2 = str.maketrans(alph2, alph2[-step:]+alph2[:-step])
# merge both translation tables
t.update(t2)
print(st.translate(t))
Output:
utGtGt
You give it the original string and an equal long string to map letters to and apply that dictionary using str.translate(dictionary).
The sliced strings equate to:
print(alphabets)
print(alphabets[-step:]+alphabets[:-step])
abcdefghijklmnopqrstuvwxyz
tuvwxyzabcdefghijklmnopqrs
which is what your step is for.
See Understanding slice notation if you never saw string slicing in use.
by processing each charater and checking it's cardinal no and making calculation accordingly help you to reach the result
def func(string, size):
if size%26==0:
size=26
else:
size=size%26
new_str = ''
for char in string:
if char.isupper():
if ord(char)-size<ord('A'):
new_str+=chr(ord(char)-size+26)
else:
new_str+=chr(ord(char)-size)
elif char.islower():
if ord(char)-size<ord('a'):
new_str+=chr(ord(char)-size+26)
else:
new_str+=chr(ord(char)-size)
return new_str
res =func('baNaNa', 7)
print(res)
# output utGtGt
Here's a simple solution that makes use of the % modulo operator to shift letters backwards.
It basically collects all of the letters in a reverse index lookup dictionary, so looking up letter positions is O(1) instead of using list.index(), which is linear O(N) lookups.
Then it goes through each letter and calculates the shift value from the letter index e.g. for the letter a with a shift value of 7, the calculation will be (0 - 7) % 26, which will give 19, the position of u.
Then once you have this shift value, convert it to uppercase or lowercase depending on the case of the original letter.
At the end we just str.join() the result list into one string. This is more efficient than doing += to join strings.
Demo:
from string import ascii_lowercase
def letter_backwards_shift(word, shift):
letter_lookups = {letter: idx for idx, letter in enumerate(ascii_lowercase)}
alphabet = list(letter_lookups)
result = []
for letter in word:
idx = letter_lookups[letter.lower()]
shifted_letter = alphabet[(idx - shift) % len(alphabet)]
if letter.isupper():
result.append(shifted_letter.upper())
else:
result.append(shifted_letter.lower())
return ''.join(result)
Output:
>>> letter_backwards_shift('baNaNa', 7)
utGtGt
I would probably go with #Patrick Artner's pythonic solution. I just showed the above implementation as a learning exercise :-).
Related
So I have a list of numbers (answer_index) which correlate to the index locations (indicies) of a characters (char) in a word (word). I would like to use the numbers in the list as index inputs later (indexes) on in code to replace every character except my chosen character(char) with "*" so that the final print (new_word) in this instance would be (****ee) instead of (coffee). it is important that (word) maintains it's original value while (new_word) becomes the modified version. Does anyone have a solution for turning a list into valid index inputs? I will also except easier ways to meet my goal. (Note: I am extremely new to python so I'm sure my code looks horrendous) Code below:
word = 'coffee'
print(word)
def find(string, char):
for i, c in enumerate(string):
if c == char:
yield i
string = word
char = "e"
indices = (list(find(string, char)))
answer_index = (list(indices))
print(answer_index)
for t in range(0, len(answer_index)):
answer_index[t] = int(answer_index[t])
indexes = [(answer_index)]
new_character = '*'
result = ''
for i in indexes:
new_word = word[:i] + new_character + word[i+1:]
print(new_word)
You hardly ever need to work with indices directly:
string = "coffee"
char_to_reveal = "e"
censored_string = "".join(char if char == char_to_reveal else "*" for char in string)
print(censored_string)
Output:
****ee
If you're trying to implement a game of hangman, you might be better off using a dictionary which maps characters to other characters:
string = "coffee"
map_to = "*" * len(string)
mapping = str.maketrans(string, map_to)
translated_string = string.translate(mapping)
print(f"All letters are currently hidden: {translated_string}")
char_to_reveal = "e"
del mapping[ord(char_to_reveal)]
translated_string = string.translate(mapping)
print(f"'{char_to_reveal}' has been revealed: {translated_string}")
Output:
All letters are currently hidden: ******
'e' has been revealed: ****ee
The easiest and fastest way to replace all characters except some is to use regular expression substitution. In this case, it would look something like:
import re
re.sub('[^e]', '*', 'coffee') # returns '****ee'
Here, [^...] is a pattern for negative character match. '[^e]' will match (and then replace) anything except "e".
Other options include decomposing the string into an iterable of characters (#PaulM's answer) or working with bytearray instead
In Python, it's often not idiomatic to use indexes, unless you really want to do something with them. I'd avoid them for this problem and instead just iterate over the word, read each character and and create a new word:
word = "coffee"
char_to_keep = "e"
new_word = ""
for char in word:
if char == char_to_keep:
new_word += char_to_keep
else:
new_word += "*"
print(new_word)
# prints: ****ee
I want to create an encryption script that encrypts the string given to it, here's how it went.
# First, I created a list of the string I got
string = '16271'
string_list = []
for letter in string:
string_list.append(letter)
Then, I have a list called encrypt_list which the letters which I want to add in order to encrypt my string.
So, I use the following code to add a random letter from the encrypt_list after each letter/component in the string_list and then join the list and print as a string.
for i in range(0, len(string) - 1):
string_list.insert(for letter in string_list: string_list.index(letter) + 1, encrypt_list[random.randint(0, len(encrypt_list) - 1)])
print("The encrypted string is: ")
print(''.join(string_list))
I expected the output to be: 1A6b2n781 (I bolded the letter to show my actual string in it) But I am getting an error, that I cannot use the for loop in the insert function, and I cannot find another way of doing that, please help. Hope I make my problem clear
Not sure what your encrypted_list looks like, but if it's a list of letters, this would work:
import random
string = '16271'
encrypted_list = ['r', 't', 's', 'o', 'j', 'e']
encrypted_string = ''.join([s + random.choice(encrypted_list) for s in string])
Something like this?
# First, I created a list of the string I got
import random
string = '16271'
string_list = []
for letter in string:
string_list.append(letter)
the_other_list = ['lorem', 'dorem', 'forem', 'borem']
for i in range(0, len(the_other_list)):
the_other_list[i] = string_list[random.randint(0, len(string_list) - 1)] + the_other_list[i]
print(''.join(the_other_list))
Result example: 1lorem2dorem2forem7borem
You can use a for loop, adding one letter to the list at a time, then adding a randomly selected letter immediately afterwards (if we're not processing the last letter in the list). I've used constants from string to define the space of characters to sample from; you can adjust this as you see fit.
This should be simpler than trying to do repeated insertion in the middle of the list (where you'd have to handle memory shifting as you're inserting, plus it'd get slower for larger texts because you'd be attempting to insert in the middle of a list).
# First, I created a list of the string I got
import random
import string
encrypt_text = string.ascii_uppercase + string.ascii_lowercase + string.digits
plaintext = '16271'
letters = []
for index, letter in enumerate(plaintext):
letters.append(letter)
if index != len(plaintext) - 1:
letters.append(random.choice(encrypt_text))
print("The encrypted string is: ")
print(''.join(letters))
Based on how you defined the problem, I would recommend implementing it as a generator:
import random
import string
def _gen_string(s):
alphabet = string.ascii_letters
for c in s:
yield c
yield random.choice(alphabet)
Then you can use that as the basis for your encryption:
def encrypt(s):
return ''.join(_gen_string(s))
encrypt('16271')
# 1J6P2U7Z1i
Of course, this is not really encryption. It's obscurity and obscurity is not a form of security that should be relied upon :-)
I want to multiply letter of string by digits of number. For example for a word "number" and number "123"
output would be "nuummmbeerrr". How do I create a function that does this? My code is not usefull, because it doesn't work.
I have only this
def new_word(s):
b=""
for i in range(len(s)):
if i % 2 == 0:
b = b + s[i] * int(s[i+1])
return b
for new_word('a3n5z1') output is aaannnnnz .
Using list comprehension and without itertools:
number = 123
word = "number"
new_word = "".join([character*n for (n, character) in zip(([int(c) for c in str(number)]*len(str(number)))[0:len(word)], word)])
print(new_word)
# > 'nuummmbeerrr'
What it does (with more details) is the following:
number = 123
word = "number"
# the first trick is to link each character in the word to the number that we want
# for this, we multiply the number as a string and split it so that we get a list...
# ... with length equal to the length of the word
numbers_to_characters = ([int(c) for c in str(number)]*len(str(number)))[0:len(word)]
print(numbers_to_characters)
# > [1, 2, 3, 1, 2, 3]
# then, we initialize an empty list to contain the repeated characters of the new word
repeated_characters_as_list = []
# we loop over each number in numbers_to_letters and each character in the word
for (n, character) in zip(numbers_to_characters, word):
repeated_characters_as_list.append(character*n)
print(repeated_characters_as_list)
# > ['n', 'uu', 'mmm', 'b', 'ee', 'rrr']
new_word = "".join(repeated_characters_as_list)
print(new_word)
# > 'nuummmbeerrr'
This will solve your issue, feel free to modify it to fit your needs.
from itertools import cycle
numbers = cycle("123")
word = "number"
output = []
for letter in word:
output += [letter for _ in range(int(next(numbers)))]
string_output = ''.join(output)
EDIT:
Since you're a beginner This will be easier to understand for you, even though I suggest reading up on the itertools module since its the right tool for this kind of stuff.
number = "123"
word = "number"
output = []
i = 0
for letter in word:
if(i == len(number)):
i = 0
output += [letter for _ in range(int(number[i]))]
i += 1
string_output = ''.join(output)
print(string_output)
you can use zip to match each digit to its respective char in the word (using itertools.cycle for the case the word is longer), then just multiply the char by that digit, and finally join to a single string.
try this:
from itertools import cycle
word = "number"
number = 123
number_digits = [int(d) for d in str(number)]
result = "".join(letter*num for letter,num in zip(word,cycle(number_digits)))
print(result)
Output:
nuummmbeerrr
I've encountered a small problem with a simple shift deciphering.
N,K = [int(s) for s in input().split()]
myres = []
alph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
myalph = [a for a in alph]
for i in range(N):
s = input()
mylist = [d for d in str(s)]
for b in range(len(mylist)):
for c in range(len(myalph)):
if mylist[b] == myalph[c]:
mylist[b] = myalph[c-K]
print(myalph[c-K], c-K, b, c)
myres = myres + mylist
Res = [str(i) for i in myres]
print("".join(Res))
The idea is for every character of my input string to be replaced with a different character from the alphabet that's been shifted by a given key (K).
The problem occurs when c-K < 0 and the replacing key is taken from the back of the list. Then the loop is being iterated twice.
If the key is 3 and I input A instead of getting X, I'm getting U as the first iteration gives X but then X is also iterated and becomes U.
Your mistake is looping over all the letters in alph:
The for loop tests all the letters of the alphabet, in order, and 'A' is matched. You set mylist[b] to 'X' (0 - 3 is -3 and myalph[-3] is 'X'.
The loop then continues to test all the other letters of the alphabet against mylist[b], so eventually it gets to 'X', sees that the letter matches and sets mylist[b] to 'U'.
The loop continues to test the remaining letters of the alphabet against mylist[b], and reaches the end without further matches.
At the very least you need to break out of the loop when you have shifted a letter.
But rather than loop, you could use the str.find() method (directly on the alph string) to find a matching index for the letter; it'll be set to -1 if the letter is not found at all:
for b in range(len(mylist)):
c = alph.find(s[b])
if c > -1: # the letter exists
s[b] = alph[c - K]
Aside from that, there are some other improvements you could make:
You can loop over and index into strings directly, there is no need to turn alph into a list here. When you do need to to turn a string into a list of individual characters, you should use list(stringobject). So mylist = list(s) would suffice.
myres is already a list of strings, there is no need to convert each to a string again.
Rather than put all the letters from s into a list, then adding the whole mylist list to res to myres, you could just directly append each letter you processed to myres; that also removes the need to alter myres.
Python variable names do not need to be limited to single characters. Use more descriptive names so that it is easier to understand what your code does when you return to it later.
Taken together, that'd lead to:
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
parts, key = [int(s) for s in input().split()]
results = []
for part in range(parts):
characters = input()
for character in enumerate(characters):
letter_idx = alphabet.index(character)
if letter_idx > -1:
# this is a letter in the alphabet, shift it with the key
character = alphabet[letter_idx - key]
results.append(character)
print("".join(results))
I want to write a program that prints the longest substring in alphabetical order.
And in case of ties, it prints the first substring.
Here is what I wrote
import sys
s1 = str(sys.argv[1])
alpha = "abcdefghijklmnopqrstuvwxyz"
def longest_substring(s1):
for i in range(len(alpha)):
for k in range(len(alpha)):
if alpha[i:k] in s1:
return alpha[i:k]
print("Longest substring in alphabetical order:", longest_substring(s1))
However, it does not work and I do not know how to do the second part.
Can you help me, please?
Here is what your code should look like to achieve what you want:
#!/usr/bin/env python3.6
import sys
s1 = str(sys.argv[1])
alpha = "abcdefghijklmnopqrstuvwxyz"
subs = []
def longest_substring(s1):
for i in range(len(alpha)):
for k in range(len(alpha)):
if alpha[i:k] in s1:
subs.append(alpha[i:k])
return max(subs, key=len)
print("Longest substring in alphabetical order:", longest_substring(s1))
You were returning right out of the function on the first alphabetically ordered substring you found. In my code, we add them to a list then print out the longest one.
Assume that substring contains 2 or more characters in alphabetical order. So that you should not only return the first occurrence but collect all and find longest. I try to keep your idea the same, but this is not the most efficient way:
def longest_substring(s1):
res = []
for i in range(len(alpha) - 2):
for k in range(i + 2, len(alpha)):
if alpha[i:k] in s1:
res.append(alpha[i:k])
return max(res, key=len)
You re-write a version of itertools.takewhile to take a binary compare function instead of the unary one.
def my_takewhile(predicate, starting_value, iterable):
last = starting_value
for cur in iterable:
if predicate(last, cur):
yield cur
last = cur
else:
break
Then you can lowercase the word (since "Za" isn't in alphabetical order, but any [A-Z] compares lexicographically before any [a-z]) and get all the substrings.
i = 0
substrings = []
while i < len(alpha):
it = iter(alpha[i:])
substring = str(my_takewhile(lambda x,y: x<y, chr(0), it))
i += len(substring)
substrings.append(substring)
Then just find the longest substring in substrings.
result = max(substrings, key=len)
Instead of building a list of all possible substring slices and then checking which one exists in the string, you can build a list of all consecutive substrings, and then take the one with the maximum length.
This is easily done by grouping the characters using the difference between the ord of that character and an increasing counter; successive characters will have a constant difference. itertools.groupby is used to perform the grouping:
from itertools import groupby, count
alpha = "abcdefghijklmnopqrstuvwxyz"
c = count()
lst_substrs = [''.join(g) for _, g in groupby(alpha, lambda x: ord(x)-next(c))]
substr = max(lst_substrs, key=len)
print(substr)
# abcdefghijklmnopqrstuvwxyz
As #AdamSmith commented, the above assumes the characters are always in alphabetical order. In the case they may not be, one can enforce the order by checking that items in the group are alphabetical:
from itertools import groupby, count, tee
lst = []
c = count()
for _, g in groupby(alpha, lambda x: ord(x)-next(c)):
a, b = tee(g)
try:
if ord(next(a)) - ord(next(a)) == -1:
lst.append(''.join(b))
except StopIteration:
pass
lst.extend(b) # add each chr from non-alphabetic iterator (could be empty)
substr = max(lst, key=len)
back up and look at this problem again.
1. you are looking for a maximum and should basically (pseudo code):
set a max to ""
loop through sequences
if new sequence is bigger the max, then replace max
find the sequences you can be more efficient if you only step though the input characters once.
Here is a version of this:
def longest_substring(s1):
max_index, max_len = 0, 0 # keep track of the longest sequence here
last_c = s1[0] # previous char
start, seq_len = 0, 1 # tracking current seqence
for i, c in enumerate(s1[1:]):
if c >= last_c: # can we extend sequence in alpha order
seq_len += 1
if seq_len > max_len: # found longer
max_index, max_len = start, seq_len
else: # this char starts new sequence
seq_len = 0
start = i + 1
last_c = c
return s1[max_index:max_index+max_len]
s = 'azcbobobegghakl'
def max_alpha_subStr(s):
'''
INPUT: s, a string of lowercase letters
OUTPUT: longest substing of s in which the
letters occur in alphabetical order
'''
longest = s[0] # set variables 'longest' and 'current' as 1st letter in s
current = s[0]
for i in s[1:]: # begin iteration from 2nd letter to the end of s
if i >= current[-1]: # if the 'current' letter is bigger
# than the letter before it
current += i # add that letter to the 'current' letter(s) and
if len(current) > len(longest): # check if the 'current' length of
# letters are longer than the letters in'longest'
longest = current # if 'current' is the longest, make 'longest'
# now equal 'current'
else: # otherwise the current letter is lesser
# than the letter before it and
current = i # restart evaluating from the point of iteration
return print("Longest substring in alphabetical order is: ", longest)
max_alpha_subStr(s)