I wrote code in Python that takes a string s as input and determines the longest substring of s thats in alphabetical order. In the case where there are two strings of the same length, code outputs the first one.I'm a beginner with 1.5 weeks of experience and wrote both versions myself.  Which one is better, and why?Version 1def alphacheck(x):    '''This function checks if a given string is in Alphabetical order'''    # Creating a variable to allow me to efficiently index a string of any length    i = len(x) - 1    # Using a recursive formula and checking for the base case when the string is of less than one character    if len(x) <= 1:        return True    # This the recursive portion of the formula    else:        # This code checks if the last two characters are in alphabetical order        # By utilizing the property that the letters of the alphabet increase in value according to python        if x[i - 1] <= x[i]:        # I remove the last letter of the string to reduce the problem size            x = x[:i]            # I then repeat this on the smaller string until it reaches the base case or            # Finds out the string is not in alphabetical order and returns False            return alphacheck(x)        else:            return False# The base list that i'll be editinglist1 = []s = 'hlqrjqbjiwedjzzw'#use i to determine the number of times i'll run the loopi = int(len(s)+1)#use u to change the size of my slice of the stringu = 0itersLeft = i# counter used to slice the string by picking the 1st element then the 2nd then 3rd then...# then picking the 1st element and the 2nd element together then picking the 2nd element and the 3rd element together then....counter = 0while u <= i:    while itersLeft > 0:        #this is the code that slices up the string according the way outlined in earlier comments        x = s[counter-1:counter+u ]        list1.append(x)        itersLeft -= 1        counter += 1#I change the value of u because that's what decides how big my slices are if u is 0 the slices are length 0 if u is 1 slices are lenght 1 and so on    u += 1    itersLeft = i    counter = 0del list1[0]#This removes duplicates in the list by scanning the first list for elements and adding them to the second (empty)list if theyre not already in the secodn     listlist2 = []numMatch = 0for e1 in list1:    if e1 in list2:        #This numMatch variable is used just as a place holder since it's the else condition i need not the if        numMatch += 1    else:        list2.append(e1)list3 = []for e in list2:    if alphacheck(e) == True:        list3.append(e)list4 = list3[:]for e in list3:    if len(e) < len(list3[len(list3)-1]):        list4.remove(e)print(list1)print(list2)print(list3)print(list4)print('Longest substring in alphabetical order is: ' + list4[0] )Version 2def alphacheck(x):    '''This function checks if a given string is in Alphabetical order'''    # Creating a variable to allow me to efficiently index a string of any length    i = len(x) - 1    # Using a recursive formula and checking for the base case when the string is of less than one character    if len(x) <= 1:        return True    # This the recursive portion of the formula    else:        # This code checks if the last two characters are in alphabetical order        # By utilizing the property that the letters of the alphabet increase in value accroding to python        if x[i - 1] <= x[i]:            # I remove the last letter of the string to reduce the probelm size            x = x[:i]            # I then repeat this on the smaller string until it reaches the base case or            # Finds out the string is not in alphabetical order and returns False            return alphacheck(x)        else:            return Falselastr = ''s = 'hlqrjqbjiwedjzzw'#use i to determine the number of times i'll run the loopi = int(len(s)+1)#use u to change the size of my slice of the stringu = 0itersLeft = i# counter used to slice the string by picking the 1st element then the 2nd then 3rd then...# then picking the 1st element and the 2nd element together then picking the 2nd element and the 3rd element together then....counter = 0while u <= i:    while itersLeft > 0:        #this is the code that slices up the string according the way outlined in earlier comments        x = s[counter-1:counter+u ]        if alphacheck(x) == True:            if len(x) > len(lastr):                lastr = x        itersLeft -= 1        counter += 1#I change the value of u because that's what decides how big my slices are if u is 0 the slices are length 0 if u is 1 slices are lenght 1 and so on    u += 1    itersLeft = i    counter = 0print('Longest substring in alphabetical order is: ' + lastr )
Finding the longest substring in alphabetical order
python;beginner;algorithm;python 3.x;comparative review
Neither are that good.Both suffer from the nested loops.while u <= i:    while itersLeft > 0:        ...    itersLeft = iThis means that the algorithm suddenly has a \$O(n^2)\$ time complexity.How many times do you run the code in while u <= i?That's i amount of times. In this you have the loop while itersLeft > 0,this runs i - itersLeft amount of times.And so you this runs \$\Sigma_{\text{itersLeft}=0}^i(i - \text{itersLeft})\$ amount of times.Which is the same as \$\frac{1}{2}i(i + 1)\$.And so we can simplify that to just \$i^2\$, and so it runs roughly \$O(n^2)\$ amount of times.Instead I'd come at this to try to minimise the amount of loops.Preferably you should have one loop.To force this I'd use for item in string.After this we should aim to make all the substrings.Looking at alphacheck we know that it's a new substring if prev > item.And so we'll want to add the previous substring to a new list when this happens.And so we should have the code:def ordered_substrings(string):    substrings = []    prev = None    for item in string:        if prev is not None and prev > item:            substrings.append(...)        prev = item    return substringsSo, now we need to figure out what to add to substrings list.To do this is the hardest part of this algorithm.You need to create the substring which is all the items from the end of the last.If we make a new list to hold the creation of this substring and add to it each loop we can end up with:def ordered_substrings(string):    substrings = []    substring = []    prev = None    for item in string:        if prev is not None and prev > item:            substrings.append(''.join(substring))        substring.append(item)        prev = item    return substringsRunning this on 'abcabca' results in ['abc', 'abcabc'].This shows two problems, it doesn't add the last substring.And we're adding old substrings.To fix these should be relatively simple now, when we add the substring to substrings,we need to reset it.and at the end we need to add to substrings.Resulting in:def ordered_substrings(string):    substrings = []    substring = []    prev = None    for item in string:        if prev is not None and prev > item:            substrings.append(''.join(substring))            substring = []        substring.append(item)        prev = item    substrings.append(''.join(substring))    return substringsTo then further improve these I'd make the function a generator, which functions like range.But can allow you to reduce the amount of memory used.All you need to do is remove substrings, and change substrings.append to yield.def ordered_substrings(string):    substrings = []    substring = []    prev = None    for item in string:        if prev is not None and prev > item:            yield ''.join(substring)            substring = []        substring.append(item)        prev = item    yield ''.join(substring)And finally to use it, you can use max with the key keyword as len.>>> max(ordered_substrings('abcabca'), key=len)'abc'