How to use string slicing to print a string left to right - python

I have a task uses string slicing. To do this I need to start and the end and go up. up to zero. So if it’s efg It starts at __g goes to _fg and then efg. I’m confused how to do this with string slicing in a while loop.I know to get the last character I would use -1. What I have right now does kind of what I want just not with the last letter and instead the first letter. I know it has to do with my slicing but I can’t figure it out
N=len(s)
I=0
while I< N:
Print((n-I)*” “ + s[0:i:1]

The negative index should be the starting character:
> "efg"[-1:]
"g"
> "efg"[-2:]
"fg"
> "efg"[-3:]
"efg"

I'm not entirely sure what output you were expecting, but this is the code I came up with from what I interpreted:
s = "efg"
n = len(s)
i = 0
while(i < n):
print("_" * (n-i-1) + s[((i+1) * -1):])
i+=1
Output:
__g
_fg
efg
We can print out one less than the difference of i and n number of _'s, and then slice as #chepner mentioned.
The key part of this problem is finding a way to use i and n to get the correct number of repetitions of _ and the correct slicing of s.
Since i will start at 0 and end at 2, and n = 3, and in this example the number of _s goes from 2 to 1 to 0, a simple equation would be: number of _s = n - i - 1.
Similarly, to slice from -1, -2, and then -3 onwards, this relationship is just equal to -(i+1).
I hope this helped! Let me know if you need any further help or clarification.

Related

Accessing two following indexes in a loop

I'm new to python (although it's more of a logical question rather than syntax question I belive), and I wonder what's the proper way to access two folowing objects in a loop.
I can't really provide a specific example without getting too cumbersome with my explanation but let's just say that I usually try to tackle this with either [index + 1] or [index - 1] and both are problematic when it comes to either the last (IndexError) or first (addresses the last position right at the beginning) iterations respectively.
Is there a well known way to address this? I haven't really seen any questions regarding this floating around so it made me think it's basic logic I'm missing here.
For example this peice of code that wouldn't have worked had I not wrapped everything with try/except, and also the second inner loop works only since it checks for identical characters, otherwise it could have been a mess.
(explanation for clarity - it recieves a string (my_string) and a number (k) and checks whether a sequence of identical characters the length of k exists in my_string)
# ex2 5
my_string = 'abaadddefggg'
sub_my_string = ''
k = 9
count3 = 0
try:
for index in range(len(my_string)):
i = 0
while i < k:
sub_my_string += my_string[index + i]
i += 1
for index2 in range(len(sub_my_string)):
if sub_my_string[index2] == sub_my_string[index2 - 1]:
count3 += 1
if count3 == k:
break
else:
sub_my_string = ""
count3 = 0
print(f"For length {k}, found the substring {sub_my_string}!")
except IndexError:
print(f"Didn't find a substring of length {k}")
Thanks a lot
First off, by definition you need to give special attention to the first or last element, because they really don't have a pair.
Second-off, I personally tend to use list-comprehensions of the following type for these cases -
[something_about_the_two_consecutive_elements(x, y) for x, y in zip(my_list, my_list[1:])]
And last but not least, the whole code snippet seems like major overkill. How about a simple one-liner -
my_string = 'abaadddefggg'
k = 3
existing_substrings = ([x * k for x in set(my_string) if x * k in my_string])
print(f'For length {k}, found substrings {existing_substrings}')
(To be adapted by one's needs of course)
Explanation:
For each of the unique characters in the string, we can check if a string of that character repeated k times appears in my_string.
set(my_string) gives a set of the unique characters over which we iterate (that's the for x in set(my_string) in the list comprehension).
Taking a character x and multiplying by k gives a string xx...x of length k.
So x * k in my_string tests whether my_string includes the substring xx...x.
Summing up the list-comprehension, we return only characters for which x * k in my_string is True.
If I am understanding what you are trying to achieve, I would approach this differently using string slices and a set.
my_string = "abaadddefggg"
sub_my_string = ""
k = 3
count3 = 0
found = False
for index, _ in enumerate(my_string):
if index + k > len(my_string):
continue
sub_my_string = my_string[index : index + k]
if len(set(sub_my_string)) == 1:
found = True
break
if found:
print(f"For length {k}, found the substring {sub_my_string}!")
else:
print(f"Didn't find a substring of length {k}")
Here we use:
enumerate as this usually signals that we are looking at the indices of an iterable.
Check whether the slice will be take us over the string length as there's no point in checking these.
Use the string slice to subset the string
Use the set to see if all the characters are the same.

Optimal brute force solution for finding longest palindromic substring

This is my current approach:
def isPalindrome(s):
if (s[::-1] == s):
return True
return False
def solve(s):
l = len(s)
ans = ""
for i in range(l):
subStr = s[i]
for j in range(i + 1, l):
subStr += s[j]
if (j - i + 1 <= len(ans)):
continue
if (isPalindrome(subStr)):
ans = max(ans, subStr, key=len)
return ans if len(ans) > 1 else s[0]
print(solve(input()))
My code exceeds the time limit according to the auto scoring system. I've already spend some time to look up on Google, all of the solutions i found have the same idea with no optimization or using dynamic programming, but sadly i must and only use brute force to solve this problem. I was trying to break the loop earlier by skipping all the substrings that are shorter than the last found longest palindromic string, but still end up failing to meet the time requirement. Is there any other way to break these loops earlier or more time-efficient approach than the above?
With subStr += s[j], a new string is created over the length of the previous subStr. And with s[::-1], the substring from the previous offset j is copied over and over again. Both are inefficient because strings are immutable in Python and have to be copied as a new string for any string operation. On top of that, the string comparison in s[::-1] == s is also inefficient because you've already compared all of the inner substrings in the previous iterations and need to compare only the outermost two characters at the current offset.
You can instead keep track of just the index and the offset of the longest palindrome so far, and only slice the string upon return. To account for palindromes of both odd and even lengths, you can either increase the index by 0.5 at a time, or double the length to avoid having to deal with float-to-int conversions:
def solve(s):
length = len(s) * 2
index_longest = offset_longest = 0
for index in range(length):
offset = 0
for offset in range(1 + index % 2, min(index, length - index), 2):
if s[(index - offset) // 2] != s[(index + offset) // 2]:
offset -= 2
break
if offset > offset_longest:
index_longest = index
offset_longest = offset
return s[(index_longest - offset_longest) // 2: (index_longest + offset_longest) // 2 + 1]
Solved by using the approach "Expand Around Center", thanks #Maruthi Adithya
This modification of your code should improve performance. You can stop your code when the max possible substring is smaller than your already computed answer. Also, you should start your second loop with j+ans+1 instead of j+1 to avoid useless iterations :
def solve(s):
l = len(s)
ans = ""
for i in range(l):
if (l-i+1 <= len(ans)):
break
subStr = s[i:len(ans)]
for j in range(i + len(ans) + 1, l+1):
if (isPalindrome(subStr)):
ans = subStr
subStr += s[j]
return ans if len(ans) > 1 else s[0]
This is a solution that has a time complexity greater than the solutions provided.
Note: This post is to think about the problem better and does not specifically answer the question. I have taken a mathematical approach to find a time complexity greater than 2^L (where L is size of input string)
Note: This is a post to discuss potential algorithms. You will not find the answer here. And the logic shown here has not been proven extensively.
Do let me know if there is something that I haven't considered.
Approach: Create set of possible substrings. Compare and find the maximum pair* from this set that has the highest possible pallindrome.
Example case with input string: "abc".
In this example, substring set has: "a","b","c","ab","ac","bc","abc".
7 elements.
Comparing each element with all other elements will involve: 7^2 = 49 calculations.
Hence, input size is 3 & no of calculations is 49.
Time Complexity:
First compute time complexity for generating the substring set:
<img src="https://latex.codecogs.com/gif.latex?\sum_{a=1}^{L}\left&space;(&space;C_{a}^{L}&space;\right&space;)" title="\sum_{a=1}^{L}\left ( C_{a}^{L} \right )" />
(The math equation is shown in the code snippet)
Here, we are adding all the different substring size combination from the input size L.
To make it clear: In the above example input size is 3. So we find all the pairs with size =1 (i.e: "a","b","c"). Then size =2 (i.e: "ab","ac","bc") and finally size = 3 (i.e: "abc").
So choosing 1 character from input string = combination of taking L things 1 at a time without repetition.
In our case number of combinations = 3.
This can be mathematically shown as (where a = 1):
<img src="https://latex.codecogs.com/gif.latex?C_{a}^{L}" title="C_{a}^{L}" />
Similarly choosing 2 char from input string = 3
Choosing 3 char from input string = 1
Finding time complexity of palindrome pair from generated set with maximum length:
Size of generated set: N
For this we have to compare each string in set with all other strings in set.
So N*N, or 2 for loops. Hence the final time complexity is:
<img src="https://latex.codecogs.com/gif.latex?\sum_{a=1}^{L}\left&space;(&space;C_{a}^{L}&space;\right&space;)^{2}" title="\sum_{a=1}^{L}\left ( C_{a}^{L} \right )^{2}" />
This is diverging function greater than 2^L for L > 1.
However, there can be multiple optimizations applied to this. For example: there is no need to compare "a" with "abc" as "a" will also be compared with "a". Even if this optimization is applied, it will still have a time complexity > 2^L (For the most cases).
Hope this gave you a new perspective to the problem.
PS: This is my first post.
You should not find the string start from the beginning of that string, but you should start from the middle of it & expand the current string
For example, for the string xyzabccbalmn, your solution will cost ~ 6 * 11 comparison but searching from the middle will cost ~ 11 * 2 + 2 operations
But anyhow, brute-forcing will never ensure that your solution will run fast enough for any arbitrary string.
Try this:
def solve(s):
if len(s)==1:
print(0)
return '1'
if len(s)<=2 and not(isPalindrome(s)):
print (0)
return '1'
elif isPalindrome(s):
print( len(s))
return '1'
elif isPalindrome(s[0:len(s)-1]) or isPalindrome(s[1:len(s)]):
print (len(s)-1)
return '1'
elif len(s)>=2:
solve(s[0:len(s)-1])
return '1'
return 0

Why doesn't my string reversal my_str[len(my_str)-1:-1:-1] produce output?

TL;DR: Why doesn't my_str[6:-1:-1] work while my_str[6::-1] does?
I have a string:
my_str="abcdefg"
I have to reverse parts of the string, which I generally do with
my_str[highest_included_index:lowest_included_index-1:-1]
For example,
# returns the reverse of all values between indices 2 and 6 (inclusive)
my_str[6:2-1:-1]
'gfedc'
This pattern breaks down if I want to include the 0 index in my reversal:
my_str[6:0-1:-1] # does NOT work
''
my_str[6:-1:-1] # also does NOT work
''
my_str[6::-1] # works as expected
'gfedcba'
Do I have to add an edge case that checks if the lowest index I want to include in a reversed string is zero and not include it in my reversal? I.e., do I have to do
for low in range(0,5):
if low == 0:
my_str[6::-1]
else:
my_str[6:low-1:-1]
That seems... unwieldy and not what I expect from python.
Edit:
The closest thing I could find to documentation of this is here:
s[i:j:k]
...
The slice of s from i to j with step k is defined as the sequence of items with index x = i + n*k such that 0 <= n < (j-i)/k. In other words, the indices are i, i+k, i+2*k, i+3*k and so on, stopping when j is reached (but never including j). When k is positive, i and j are reduced to len(s) if they are greater. When k is negative, i and j are reduced to len(s) - 1 if they are greater. If i or j are omitted or None, they become “end” values (which end depends on the sign of k). Note, k cannot be zero. If k is None, it is treated like 1.
However, this mentions nothing about a negative j being maximized to zero, which it appears is the current behavior.
my_str[6:0-1:-1] # does NOT work
''
my_str[6:-1:-1] # also does NOT work
''
Yes, they do work -- as documented. A negative index is the sequence position, counting from the right. These expressions are equivalent to
my_str[6:-1:-1]
Since 6 and -1 denote the same position, the result is an empty string. However, if you give a value that is at or past the string start, such as
my_str[6:-10:-1]
then you see the expected reversal, just as if you'd specified 6::-1
Yes, you have to make a special case for the discontinuity in indexing.
#something like this?
x='my test string'
print(x[3:7][::-1])

How to increment a variable inside of a list comprehension

I have a python function which increments the character by 1 ascii value if the index is odd and decreases the ascii value by 1 if index is even .I want to convert it to a function which do the same increment and decrement but in the next set i want to increment by 2 and then decrement by -2 then by 3 and -3 and so on..
What Iam trying to do is to increment the counter variable by 1 each time an even index occurs after performing the ascii decrement.
I also dont want to do it with a for loop is there any way to do it in the list comprehension itself?
In my function if the input is
input :'abcd' output: is 'badc' what i want is 'baeb'
input :'cgpf' output: is 'dfqe' what i want is 'dfrd'
def changer(s):
b=list(s)
count=1
d=[chr(ord(b[i])+count) if i%2==0 else chr(ord(b[i])-count) for i in range(0,len(b))]
return ''.join(d)
I need something like count++ as show but sadly python dont support it.
def changer(s):
b=list(s)
count=1
d=[chr(ord(b[i])+count) if i%2==0 else chr(ord(b[i])-count++) for i in range(0,len(b))]
return ''.join(d)
Here is the runnable code
If I correctly understood what you're after, something like this (compact form) should do:
def changer(s):
return "".join(chr(ord(c) + ((i // 2) + 1) * (-1 if i % 2 else 1))
for i, c in enumerate(s))
We get index and character from string by means of enumerate() and use that to feed a generator comprehension (as asked for) far of index (i) and character (c) from the string.
For each ASCII value of c, we add result of integer division of i (incremented by one as index was 0 based) by 2 and multiply it by (-1 if i % 2 else 1) which flips +/- based on even/odd number: multiply by -1 when modulo of i division by 2 is non-zero (and bool() evaluates as True), otherwise use 1.
Needless to say: such comprehension is not necessarily easy to read, so if you'd use it in your script, it would deserve a good comment for future reference. ;)
Combine a stream of plus/minus with the string.
import itertools
s = 'abcdefg'
x = range(1,len(s)+1)
y = range(-1,-len(s)+1,-1)
z = itertools.chain.from_iterable(zip(x,y))
r = (n + ord(c) for (n,c) in zip(z,s))
''.join(chr(n) for n in r)
I don't think I'd try to put it all in one line. Uses generator expressions instead of list comprehensions.
Try the code below,
def changer(s):
return ''.join([chr(ord(j) + (i // 2 + 1)) if i % 2 == 0 else chr(ord(j) - (i // 2 + 1)) for i, j in enumerate(s)])
s = 'abcd'
changer(s)
output
baeb

What a solitaire negative number on range does?

I was doing an exercise from John Zelle's book on Python, he asked to do the Fibonacci sequence using a loop function.
After I didn't manage to get it done, I gave a look at his resolution, which was this:
n = int(input("Enter the value of n: "))
curr, prev = 1, 1
for i in range(n-2):
curr, prev = curr+prev, curr
print("The nth Fibonacci number is", curr)
While I did understand part of what he did, the part that I missed was the (n-2) range.
I gave a look here on Stackoverflow to see about this and people say that a negative number on the range goes back to the end of the list. But in this case, if the user prompts 1, the result will be range(-1).
My guess was that the author did that so that the for loop didn't sum the first two values of the Fibonacci sequence, since they are both 1, and only after the user prompts 2 and forth, the loop actually starts adding up. Am I right on my guess?
If you enter 0 or 1 for this, the code does not enter the loop, and the result is the initial value of curr, that being 1. For any higher value, the loop will iteratively compute the proper value.
Your memory of negative values is a little bit off: a negative index will work from the opposite end of an iterable (e.g. list, tuple, string). A range is not quite in that class; the result in this case is an empty range.
CLARIFICATION after OP comment
I see your confusion. range returns an iterable of the given values. However, it looks like you've confused the limits with the index. Let's work with a general form:
r = range(left, right, step)
r[pos]
left* defaults to 0; **step defaults to 1
Here are some examples:
>>> r = range(0, 20, 2)
>>> r[-1]
18
>>> r = range(0, -1)
>>> r
[]
>>> r = range(0, -10, -2)
>>> r
[0, -2, -4, -6, -8]
>>> r[-2]
-6
Note the second and third examples, where we use negative values for endpoints. There's a distinction between a negative endpoint and a negative index. The endpoint is used to build the list; if the endpoints aren't in the order implied by the step, then the resulting range is the empty list. range(0, -1) is such an example.
Once the list is built, such as with range(0, 20, 2), then a reference into that list with a negative index will count from the right end of the list. Note the third example, making a list that goes "backward", 0 down to -8. A negative index in this case also works from the right. The negative right-end value, the negative step, and the negative index are three distinct usages.
Does that clear up things?
>>> range(-1)
range(0, -1)
So the for loop is not entered if n is 1 or 2 and curr (which is set to 1) is the result.

Categories

Resources