简体   繁体   中英

Return maximum value after K swaps using recursion

I am attempting following problem from geeks for geeks, problem link; [enter link description here][1]

Here is my python code to solve it;

def swap_kdigits(s, k, maximum):
    if k == 0:
       return
    for i in range(0, len(s) - 1): 
   /* find maximum element on the right */
       maxc = s[i]
       for j in range(i + 1, len(s)):
          if int(s[j]) > int(maxc):
            maxc = s[j]

       if maxc != s[i]:
         idx = s.index(maxc)
         ll = list(s)
        # do the swap
         ll[i], ll[idx] = ll[idx], ll[i]
         s = ''.join(ll)
         maximum = max(int(s), maximum) /* update maximum values*/
        # make  a recursive call on the new string
         swap_kdigits(s, k - 1, maximum)
        # backtrack
         ll[i], ll[idx] = ll[idx], ll[i]

         s = ''.join(ll)


def main_fn(s, k):
    maximum = int(s) /*initialize maximum variable*/
    return swap_kdigits(s, k, maximum) /* call helper function */

Can I please get some help on how to return the maximum value obtained to the calling function? As you can see, right now nothing is been returned. Help is appreciated. [1]: https://practice.geeksforgeeks.org/problems/largest-number-in-k-swaps-1587115620/1/?page=2&category[]=Strings&sortBy=submissions

Edited Code Without Recursion

def swap_kdigits(s, k):
# define current max
   maximum = int(s)

   for i in range(0, len(s) - 1):
      maxc = s[i]
      if k == 0:
        break
      for j in range(i + 1, len(s)):

         if int(s[j]) > int(maxc):
            maxc = s[j]
   

      if maxc != s[i]:
        k -= 1
        idx = s[i + 1:].rfind(maxc) + i + 1  # find last occurrence
        ll = list(s)
        # do the swap
        ll[i], ll[idx] = ll[idx], ll[i]
        s = ''.join(ll)
        # find the new max
        maximum = max(int(s), maximum)
       
  return maximum

As I don't have a GeeksForGeeks account, I cannot login into the site and see for myself if there's an actual answer to this question. I'm telling this, because I don't know why you came up with a recursive function for the solution.

After testing your script and making some modifications, I realized your script should work without any recursion. That's right, you made a procedural solution. But why it's not working?

Reason 1) Where is the string s being updated? From what I saw, you are updating the sequence s on this line: ll[i], ll[idx] = ll[idx], ll[i] . That's ok, but note this doesn't need any recursion. There's no point in calling swap_kdigits again from the for-loop.

strings in python are immutable objects. Unless you return the string sequence from a swap_kdigits call, you're never really updating the string s on any deeper levels of recursive calls. You are only modifying it on the root level, and again, never returning anything. That's why your script returns nothing.

Reason 2) If recursive calls are pointless here, where should you use k ? My guess is whenever a swap is made towards the final maximum sequence.

Reason 3) About this line: maximum = max(int(s), maximum) . Sure, it makes sense. You're always walking towards the maximum value. But will int(s) be always higher than maximum ?

As a short answer: I don't know, didn't tested it so far... So in order to contain possible errors with the script, I've placed a safeguard using your backtracking line in the script below.

Reason 4) On this line: idx = s.index(maxc) , this line should be idx = s[i + 1:].index(maxc) + i + 1 . Confusing? I bet, but it makes sense: on the for-loop you're seeking the maxc on the sequence after index i . So, when seeking the index of maxc , you should pass only the subset where maxc is present, not the whole string.

If you didn't do this, your script would give you a wrong result when testing on this sequence: "3435335" . The reason are the repeated characters.

Finally, here's the working version of your script. It's not recursive anymore because as I explained before, it never needed recursion the way it was written.

def swap_kdigits(s, k, maximum):
    # Iterate over every character of the sequence, except the last character
    for i in range(0, len(s) - 1): 
        maxc = s[i]

        # If the k number of swaps are exceeded, stop the algorithm
        if (k == 0):
            break

        # Find the maximum character on the subset s[i+1:]
        for j in range(i + 1, len(s)):
            if int(s[j]) > int(maxc):
                maxc = s[j]

        if maxc != s[i]:
            # Remember the for-loop with j?
            # We are not seeking the maxc in the entire string,
            # only in the subset starting from index: i+1
            idx = s[i + 1:].index(maxc) + i + 1
            ll = list(s)

            # Swap character to obtain a new maximum sequence
            ll[i], ll[idx] = ll[idx], ll[i]
            s = ''.join(ll)
            
            # This is a safe guard.
            #
            # In the examples provided by the GeeksForGeeks, the
            # else block is never executed.
            #
            # However, what if int(s) is not really the maximum
            # number?
            #
            # Then, we make a backtrack to return to the previous
            # maximum sequence.
            if (int(s) > maximum):
                # Edit: update maximum number
                maximum = int(s)

                # Made a swap, decrease k
                k -= 1
            else:
                # Go back to the previous sequence
                ll[i], ll[idx] = ll[idx], ll[i]
                s = ''.join(ll)


    # Return the maximum sequence
    return s


def main_fn(s, k):
    maximum = int(s)
    return swap_kdigits(s, k, maximum)

if __name__ == '__main__':
    print(main_fn("3435335", 3))

I'm editing this answer to show you how I would make a recursive solution to this example:

def rec_swap(seq, k, offset=0):
    # Check if the number of swaps is still valid, or
    # if the offset has reached the end of the sequence
    if (k > 0 and offset < (len(seq)-1)):

        # Select maxc from a subset of right characters
        subset = seq[offset:]

        # Find the max character on the subset
        maxc = max(list(subset), key=lambda c: int(c))

        # Find the index of maxc on the subset
        maxi = subset.index(maxc)

        # Transform maxi to relate to the whole sequence seq
        maxi += offset

        # Cannot swap same character
        if (maxi != offset):
            # Create a previous of the swapped sequence
            # swapped = swap(seq, offset, maxi)
            #
            # Using this print we can see how many iterations it took
            # to find the maximum sequence. It might be less than k.
            print('SWAP: ', seq[offset], seq[maxi])
            swapped = seq[:offset] + seq[maxi] + seq[offset+1:maxi] + seq[offset] + seq[maxi+1:]

            # Only go deep if next sequence is higher than current one
            if (int(swapped) > int(seq)):
                # Return deepest, or maximum, sequence possible:
                #
                # As we made a swap on this recursion level, k is decreased by -1
                # As we move one character to the right, offset is increased by 1
                return rec_swap(swapped, k-1, offset+1)
    
    # If there's nothing to do, return current sequence
    return seq

def main():
    result = rec_swap("3435335", 3)
    print('RESULT: ', result)

if __name__ == '__main__':
    main()

In this recursive example, you can see I do not rely on altering the sequence seq as if passed by reference. I simply return seq at the end of the recursive call, or when I go down one level. This allows me to always return the deepest recursion sequence.

As strings in python are immutable data, there's no need for backtracking.

Also, instead of making a for-loop to find maxc , I used the built-in function max(target, key) , using a lambda on the key paramter to iterate over the list, and find the max character (when converted to an int).

I left this line on purpose print('SWAP: ', seq[offset], seq[maxi]) . When the recursive function is called, this print shows the actual number of swap operations made to the sequence seq .

Output of recursive script:

SWAP:  3 5
SWAP:  4 5
SWAP:  3 4
RESULT:  5543333

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM