Always have this error 'IndexError: string index out of range', when i have taken into consideration of index range

Viewed 100

Need to write a program that prints the longest substring of variable, in which the letters occur in alphabetical order.

eg. s = 'onsjdfjqiwkvftwfbx', it should returns 'dfjq'.

as a beginner, code written as below:

y=()
z=()
for i in range(len(s)-1):
    letter=s[i]
    while s[i]<=s[i+1]:
        letter+=s[i+1]
        i+=1
    y=y+(letter,)
    z=z+(len(letter),)
print(y[z.index(max(z))])

However, above code will always return

IndexError: string index out of range.

It will produce the desired result until I change it to range(len(s)-3).

Would like to seek advice on:

  1. Why range(len(s)-1) will lead to such error message? In order to take care of index up to i+1, I have already reduce the range value by 1. my rationale is, if the length of variable s is 14, it has index from 0-13, range(14) produce value 0-13. However as my code involves i+1 index, range is reduced by 1 to take care of this part.

  2. How to amend above code to produce correct result. if s = 'abcdefghijklmnopqrstuvwxyz', above code with range(len(s)-3) returns IndexError: string index out of range again. Why? what's wrong with this code?

Any help is appreciated~

2 Answers

Te reason for the out of range index is that in your internal while loop, you are advancing i without checking for its range. Your code is also very inefficient, as you have nested loops, and you are doing a lot of relatively expensive string concatenation. A linear time algorithm without concatenations would look something like this:

s = 'onsjdfjqiwkvftwfbcdefgxa'

# Start by assuming the longest substring is the first letter
longest_end = 0
longest_length = 1
length = 1

for i in range(1, len(s)):
    if s[i] > s[i - 1]:
        # If current character higher in order than previous increment current length
        length += 1
        if length > longest_length:
            # If current length, longer than previous maximum, remember position
            longest_end = i + 1
            longest_length = length
    else:
        # If not increasing order, reset current length
        length = 1

print(s[longest_end - longest_length:longest_end])

Regarding "1": Actually, using range(len(s)-2) should also work.

The reason range(len(s)-1) breaks:

For 'onsjdfjqiwkvftwfbx', the len() will be equal to 18. Still, the max index you can refer is 17 (since indexing starts at 0). Thus, when you loop through "i"'s, at some point, i will increase to 17 (which corresponds to len(s)-1) and then try access s[i+1] in your while comparison (which is impossible).

Regarding "2": The following should work:

current_output = ''
biggest_output = ''
for letter in s:
    if current_output == '':
        current_output += letter
    else:
        if current_output[-1]<=letter:
            current_output += letter
        else:
            if len(current_output) > len(biggest_output):
                biggest_output = current_output
            current_output = letter

if len(current_output) > len(biggest_output):
    biggest_output = current_output
    
print(biggest_output)
Related