How to format a string into a shape? - python

I want to be able to print a string and format it into a shape. In the code here it formats into a right triangle, but I wanna do other shapes too. The problem is I can't get the string to truncate at each line and continue on, it simply loops at the first character.
this is what it looks like
hhhhhhhhhhhhhhh
hhhhhhhhhhhhh
hhhhhhhhhhh
hhhhhhhhh
hhhhhhh
hhhhh
hhh
h
but I want it to look like this
hellowor
ldhowar
eyout
oday
?
I've been struggling to wrap my head around this concept a lot lately, I can't seem to loop functions within functions properly. I think I'm probably missing some key part of knowledge for indexes or for loops that's stopping me. But if you could show me here, I might be able to learn a bit more about it. I've tried googling this issue to no avail. I appreciate any help.
Here's my code thus far:
text = ('hello world how are you today?')
def word():
for c in text:
return c
def triangle(i, t = 0):
if i == 0:
return 0
else:
print '' * (t + 1) + word() * (i * 2 - 1)
return triangle (i - 1, t + 1)
triangle (8)
edit:
the other thing I added was this:
def triangle(i, t = 0):
if i == 0:
return 0
else:
for c in text:
print '' * (t + 1) + word() * (i * 2 - 1)
return triangle (i - 1, t + 1)
but it yields the same problem, where by it only prints the first letter from 'text'.
How do I loop through each letter?

Thanks. The basic answer is that you're making this too complicated. Start at the front of the string with your initial row; pass the remainder to a recursive call. Don't bother to take individual characters out of the string: just grab the subset you need.
Note that this has two base cases: Either size hits 0, or you run out of message before that.
def triangle(message, size):
# Size = 0: we're done; ran out of rows
if size == 0:
return
# Not enough message left: print it all
if size >= len(message):
print message
# print "size" characters and go to next line
else:
print message[:size]
triangle(message[size:], size-1)
text = "hello world how are you today?"
triangle(text, 8)
print ""
triangle(text, 7)
Output:
hello wo
rld how
are y
ou to
day?
hello w
orld h
ow ar
e yo
u t
od
a
STRING SLICES
The general form is
str[start : end : step]
This gets you the substring from str[start] through str[end-1], inclusive. If you omit any of the arguments, the defaults are
start = 0
end = -1 (through the end of the string)
step = 1
Since we rarely have regular skips through a string, the step argument is almost always defaulted to 1.

Related

How to start again at the beginning of the word?

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

How to remove pair of small and capital letters in a string?

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)

How can I optimize this function which is related to reversal of string?

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.

Trying to write the java code from the urlify problem in cracking the coding interview in python

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.

letter shifting program in python

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()

Categories