To apply a Vigenere coding, we have to shift the letters but not all by the same number. The key is this time a keyword which each letter gives us the shift to be done (taking A for a shift of 0, B for a shift of 1 ...).
Let's take an example to explain the method: Let's imagine that the keyword is "MATHS" and the word to code is "PYTHON".
To code P, I shift the number corresponding to M, i.e. 12 (because we start at 0 with A) which gives me B as coding for P.
Let's move on to Y: I shift it by the number corresponding to A, i.e. 0, so Y is the coding for Y here.
Let's go to T which is shifted by the number corresponding to T, i.e. 19, so T becomes M once shifted
And so on.
import string
def vigenere_cipher(msg, shift):
encrypted = ''
for i,j in zip(msg,shift):
new_index = ( string.ascii_uppercase.index(i) + string.ascii_uppercase.index(j) ) % 26
encrypted += string.ascii_uppercase[new_index]
return encrypted
print(vigenere_cipher('PYTHON', 'MATH'))
If our keyword is too short we start again at the beginning of the word, i.e. N will be shifted by the number corresponding to M.
My problem right here is actually with the last part, How I can simply say that if the keyword is too short we start again at the beginning of the word ?
Because only "PYTH" part is encrypted to "BYMO" with MATH as a key but not the "ON"
I think the main issue here is that you're zipping both msg and shift together, when you don't actually need to do so. You already understand the concept of using % to guarantee that you stay on a number smaller than your max number, so I'll modify your function to also use % to select which character from shift you want to use
import string
def vigenere_cipher(msg, shift):
encrypted = ''
shift_length = len(shift)
for i, char in enumerate(msg):
new_index = ( string.ascii_uppercase.index(char) + string.ascii_uppercase.index(shift[i % shift_length]) ) % 26
encrypted += string.ascii_uppercase[new_index]
return encrypted
print(vigenere_cipher('PYTHON', 'MATH'))
Just add the line shift = shift * (len(msg) // len(shift) + 1) at the start of the function so shift is repeated until it's longer than msg (e.g. this line turns MATH into MATHMATH)
import string
def vigenere_cipher(msg, shift):
shift = shift * (len(msg) // len(shift) + 1)
encrypted = ''
for i,j in zip(msg,shift):
new_index = (string.ascii_uppercase.index(i) + string.ascii_uppercase.index(j)) % 26
encrypted += string.ascii_uppercase[new_index]
return encrypted
print(vigenere_cipher('PYTHON', 'MATH'))
Output: BYMOAN
Basically what I'm trying to do is create a code that removes a pair of lower and capital letters. e.g. :
AbBax -» x
cCdatabasacCADde -» database
I've tried doing this but it gives me an error, maybe my train of thought is wrong.
def decode(c_p):
t_cp=[]
for i in c_p:
t_cp+=[I,]
#here I added each character from the string to a list so it would be easier to analyse each character
new_c_p=""
for c in range(len(t_cp)-1):
if not t_cp[c]==chr(ord(c)) and t_cp[c+1]==chr(ord(c) + 32) or not t_cp[c]==chr(ord(c) + 32) and t_cp[c+1]==chr(ord(c)) :
#here I analyse the index c and c+1 to know if the first character corresponds to the next in capital or vice-versa, if doesn't correspond, I add that character into new_c_p
new_c_p+=c
return new_c_p
Here's a slightly simpler approach:
def decode(c_p):
while True:
for i, pair in enumerate(zip(c_p, c_p[1:])):
up, lo = sorted(pair)
if up.lower() == lo and up == lo.upper():
c_p = c_p[:i] + c_p[i+2:]
break
else:
return c_p
decode("cCdatabasacCADde")
# 'database'
And here is an even better one that does not start all the way from the beginning every time and has actually linear time and space complexity:
def decode(c_p):
stack = []
for c in c_p:
if not stack:
stack.append(c)
else:
up, lo = sorted((stack[-1], c))
if up.lower() == lo and lo.upper() == up:
stack.pop()
else:
stack.append(c)
return "".join(stack)
I have a string: "String"
The first thing you do is reverse it: "gnirtS"
Then you will take the string from the 1st position and reverse it again: "gStrin"
Then you will take the string from the 2nd position and reverse it again: "gSnirt"
Then you will take the string from the 3rd position and reverse it again: "gSntri"
Continue this pattern until you have done every single position, and then you will return the string you have created. For this particular string, you would return: "gSntir"
And I have to repeat this entire procedure for x times where the string and x can be very big . (million or billion)
My code is working fine for small strings but it's giving timeout error for very long strings.
def string_func(s,x):
def reversal(st):
n1=len(st)
for i in range(0,n1):
st=st[0:i]+st[i:n1][::-1]
return st
for i in range(0,x):
s=reversal(s)
return s
This linear implementation could point you in the right direction:
from collections import deque
from itertools import cycle
def special_reverse(s):
d, res = deque(s), []
ops = cycle((d.pop, d.popleft))
while d:
res.append(next(ops)())
return ''.join(res)
You can recognize the slice patterns in the following examples:
>>> special_reverse('123456')
'615243'
>>> special_reverse('1234567')
'7162534'
This works too:
my_string = "String"
my_string_len = len(my_string)
result = ""
for i in range(my_string_len):
my_string = my_string[::-1]
result += my_string[0]
my_string = my_string[1:]
print(result)
And this, though it looks spaghetti :D
s = "String"
lenn = len(s)
resultStringList = []
first_half = list(s[0:int(len(s) / 2)])
second_half = None
middle = None
if lenn % 2 == 0:
second_half = list(s[int(len(s) / 2) : len(s)][::-1])
else:
second_half = list(s[int(len(s) / 2) + 1 : len(s)][::-1])
middle = s[int(len(s) / 2)]
lenn -= 1
for k in range(int(lenn / 2)):
print(k)
resultStringList.append(second_half.pop(0))
resultStringList.append(first_half.pop(0))
if middle != None:
resultStringList.append(middle)
print(''.join(resultStringList))
From the pattern of the original string and the result I constructed this algorithm. It has minimal number of operations.
str = 'Strings'
lens = len(str)
lensh = int(lens/2)
nstr = ''
for i in range(lensh):
nstr = nstr + str[lens - i - 1] + str[i]
if ((lens % 2) == 1):
nstr = nstr + str[lensh]
print(nstr)
or a short version using iterator magic:
def string_func(s):
ops = (iter(reversed(s)), iter(s))
return ''.join(next(ops[i % 2]) for i in range(len(s)))
which does the right thing for me, while if you're happy using some library code, you can golf it down to:
from itertools import cycle, islice
def string_func(s):
ops = (iter(reversed(s)), iter(s))
return ''.join(map(next, islice(cycle(ops), len(s))))
my original version takes 80microseconds for a 512 character string, this updated version takes 32µs, while your version took 290µs and schwobaseggl's solution is about 75µs.
I've had a play in Cython and I can get runtime down to ~0.5µs. Measuring this under perf_event_open I can see my CPU is retiring ~8 instructions per character, which seems pretty good, while a hard-coded loop in C gets this down to ~4.5 instructions per ASCII char. These don't seem to be very "Pythonic" solutions so I'll leave them out of this answer. But included this paragraph to show that the OP has options to make things faster, and that running this a billion times on a string consisting of ~500 characters will still take hundreds of seconds even with relatively careful C code.
I took the Java code from cracking the coding interview for the urlify problem (1.3):
URLify: Write a method to replace all spaces in a string with '%20'. You may assume that the string has sufficient space at the end to hold the additional characters, and that you are given the "true" length of the string. (Note: if implementing in Java, please use a character array so that you can perform this operation in place.)
EXAMPLE
Input: "Mr John Smith , 13
Output: "Mr%2eJohn%2eSmith"
I'm having some issues with the converted code. Here is my python code:
def urlify(str, trueLength):
spaceCount = 0
index = 0
i = 0
for i in range(0, trueLength):
if str[i] == ' ':
spaceCount += 1
print(spaceCount, "spaceCount")
index = trueLength + spaceCount * 2
if (trueLength < len(str)):
str[trueLength] = '\0'
for i in range(trueLength, 0):
if str[i] == ' ':
str[index - 1] = '0'
str[index - 2] = '2'
str[index - 3] = '%'
index = index - 3
else:
str[index - 1] = str[i]
index = index - 1
print(urlify("Mr John Smith ", 13))
I think one of the issues is
str[trueLength] = '\0'
I'm not sure what else could be an issue. I'm also a little confused about the two lines
if (trueLength < len(str)):
str[trueLength] = '\0'
so if someone could explain those lines, that would be awesome. I just wanted to fully understand Gayle's solution.
Code I found:
def urlify(string, length):
'''function replaces single spaces with %20 and removes trailing spaces'''
new_index = len(string)
for i in reversed(range(length)):
if string[i] == ' ':
# Replace spaces
string[new_index - 3:new_index] = '%20'
new_index -= 3
else:
# Move characters
string[new_index - 1] = string[i]
new_index -= 1
return string
Shorten code(more Pythonic way):
def urlify(string, real_length):
return string[:real_length].replace(' ', '%20')
Explanation:
string[:real_length]
# This limits strings to real length, in your case to 13. This will remove unnecessary end of the string.
.replace(' ', '%20')
# This replaces every space with '%20'.
About your code:
In Python, 'str' is reserved word. Do not use it.
In Python, you can't change strings. You must create new one. String item assignment is not supported. Your code is completely based on item assignment. You should instead create new string and add characters to it.
This code is really messed up. It is very hard to understand, you should find easier solution.
Your code and logic optimized:
def urlify(string, trueLength):
new_string = ''
for i in range(0, trueLength):
if string[i] == ' ':
new_string=new_string+'%20'
else:
new_string=new_string+string[i]
return new_string
My guess, justing trying to understand the code you want to understand:
Note that:
you are given the "true" length of the string.
So the trueLength can be shorter than the len(str), and you should handle this case in your code.
Let's say that I have a list of the alphabet:
ALPHABET = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
and lets say the shift positions are
0, 2, 19
if the input is a string
string = "xyz"
and I want to shift these 3 characters using the above shift positions of 0,2,19
as in shift 'x' 0 times to the right, shift 'y' 2 times to the right, and shift z 19 times to the right.
The only thing that comes to mind is something like the index() function of lists
I also see another problem. IF I shift 'z' 19 times to the right I will get an list index out of range error. If 'z' is shifted 19 times to the right I want it to become 's' which would be 19 shifts going around the list and starting from the beginning. Same thing with 'y' if I shift it to the right 2 times I want it to become 'a' etc....
Any suggestions on what to use?
So my way is more basic than TheSoundDefense but it works pretty well when you input three letters like "xyz". (Im guessing you can come up with a check to make sure they did so)
The main tool that i use is the index function which will match an item in the list and will give me the placement number for that item. Then I take that number and I add it to the numbers you gave. But then I divide it against the length and take the remainder. I don't care how many times it divides out to be, i just want the remainder because that tells me where its at in the alphabet. then I replace the letters and print them out.
ALPHABET = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
print "Please enter three letters"
x = list(raw_input("> ").upper())
length = len(ALPHABET)
first_letter = ALPHABET.index(x[0])
first_letter = (first_letter + 0) % length
x[0] = ALPHABET[first_letter]
second_letter = ALPHABET.index(x[1])
second_letter = (second_letter + 2) % length
x[1] = ALPHABET[second_letter]
third_letter = ALPHABET.index(x[2])
third_letter = (third_letter + 19) % length
x[2] = ALPHABET[third_letter]
print ''.join(x)
EDIT: I just realized I was probably answering a totally different question, because my brain doesn't understand the word "shift" properly. So instead of generating new letters, I'm generating an entirely new alphabet. Feel free to point and laugh.
For handling the out-of-range problem, you'll want to use the modulus function % to make the number "wrap around". You can use this in conjunction with slicing in order to get your shift.
ALPHABET = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
inputstring = "XYZ"
def shift(inputstr, shift1, shift2, shift3):
new_alphabet = list(ALPHABET)
shifts = [shift1, shift2, shift3]
for i in range(0,3):
inputchar = inputstr[i]
i1 = new_alphabet.index(inputchar)
i1_adjust = (i1 + shifts[i]) % len(new_alphabet)
temp_alphabet = new_alphabet[:i1] + new_alphabet[i1+1:]
new_alphabet = temp_alphabet[:i1_adjust] + [inputchar] + temp_alphabet[i1_adjust:]
print new_alphabet
# We call it here.
shift(inputstring,0,2,19)
We're basically finding the index of our character and adding our shift amount to it. Then we pull that character out of our alphabet and move along i1_adjust number of spaces to the new position. We pull the alphabet apart at that position, insert the character, and glue it back together. The code could probably be more elegant if shift1, shift2, shift3 was changed to a list of shift positions, but the proof of concept is there.
Can i solve this way: Let me know if you don't like this solution in comment, I will remove it. ( instead of down-voting )
#!/usr/bin/python
alpha = ['A','B','C','D','E','F','G','H','I',\
'J','K','L','M','N','O','P','Q','R',\
'S','T','U','V','W','X','Y','Z']
def shift_right(char, shift_inx):
dic1 = dict(zip(alpha, range(26)))
dic2 = dict(zip(range(26), alpha))
total = len(alpha)
nxt_inx = dic1[char] + shift_inx
if nxt_inx <= 25:
return dic2[nxt_inx]
else:
return dic2[nxt_inx % total]
def main():
for x,y in [('X', 0), ('Y', 2), ('Z', 19)]:
print '%s => %s => %s' % ( x, y, shift_right(x, y))
if __name__ == '__main__':
main()
Output:
X => 0 => X
Y => 2 => A
Z => 19 => S
OR
#!/usr/bin/python
alpha = ['A','B','C','D','E','F','G','H','I',\
'J','K','L','M','N','O','P','Q','R',\
'S','T','U','V','W','X','Y','Z']
def shift_right(char, shift_inx):
total = len(alpha)
nxt_inx = alpha.index(char) + shift_inx
if nxt_inx <= 25:
return alpha[nxt_inx]
else:
return alpha[nxt_inx % total]
def main():
for x,y in [('X', 0), ('Y', 2), ('Z', 20)]:
print '%s => %s => %s' % ( x, y, shift_right(x, y))
if __name__ == '__main__':
main()