In this Kata, you will be given a number and your task will be to rearrange the number so that it is divisible by `25`, but without leading zeros. Return the minimum number of digit moves that are needed to make this possible. If impossible, return `-1` ( `Nothing` in Haskell ).

For example:

More examples in test cases.

Good luck!

Difficulty: introductory

Starter code:

def solve(n):
	

Solutions:
def solve(n):
    moves = []
    for a, b in ["25", "75", "50", "00"]:
        s = str(n)[::-1]
        x = s.find(a)
        y = s.find(b, x+1 if a == "0" else 0)
        if x == -1 or y == -1:
            continue
        moves.append(x + y - (x > y) - (a == b))
        s = s.replace(a, "", 1).replace(b, "", 1)
        l = len(s.rstrip("0"))
        if l:
            moves[-1] = moves[-1] + (len(s) - l)
        elif s:
            moves.pop()
    return min(moves, default=-1)

def moves(st, ending):
    s = list(st)
    
    steps = 0
    for i in range(-1, -len(ending) - 1, -1):
        char = ending[i]
        while s[i] != char:
            # Find latest example of the missing char in the string prior to our position
            actual = [j for j, c in enumerate(s[:i]) if c == char][-1]
            # Transpose it with the character to its right
            s[actual], s[actual + 1] = s[actual + 1], s[actual]
            steps += 1

    while s[0] == '0':
        # Transpose first non-zero character with character to its left  
        actual = [j for j, c in enumerate(s) if c != '0'][0]
        s[actual - 1], s[actual] = s[actual], s[actual - 1]
        steps += 1
        
    return steps



def solve(n):
    print(f'n: {n}')
    s = str(n)
    _0, _2, _5, _7 = s.count('0'), s.count('2'), s.count('5'), s.count('7')
    
    best = not_possible = len(s) * 2 + 1
    if _0 > 1:
        # Enough 0s to end in ..00
        best = min(best, moves(s, '00'))
    if _2 and _5:
        # 2 & 5 present so we can end in ..25
        best = min(best, moves(s, '25'))
    if _5 and _0:        
        # 5 & 0 present so we can end in ..50
        best = min(best, moves(s, '50'))
    if _7 and _5:        
        # 7 & 5 present so we can end in ..75
        best = min(best, moves(s, '75'))

    if best == not_possible:
        best = -1
    return best


def solve(n):
    def inversion(s, a, b):
        x, y = s.rfind(a), s.rfind(b)
        if a == b: x = s.rfind(a, 0, y)
        # Suffix not found
        if not x > -1 < y: return float('inf')
        inv = len(s) - 2 - x + len(s) - 1 - y + (x > y)
        # Leading zeros
        if not x > 0 < y:
            t = s[1 + ({x, y} == {0, 1}):]
            if t: inv += len(s) - len(str(int(t))) - 1 - ({x, y} == {0, 1})
        return inv

    inv = min(inversion(str(n), a, b) for a, b in '00 25 50 75'.split())
    return -1 if inv == float('inf') else inv


from functools import partial

def check(s, tail):
    i = s.find(tail[1])
    if i == -1: return float('inf')
    s = s[:i] + s[i+1:]
    
    j = s.find(tail[0])
    if j == -1: return float('inf')
    s = s[:j] + s[j+1:]
    
    if not s: return i+j
    if s == '0'*len(s): return float('inf')
    return i + j + next(i for i,c in enumerate(reversed(s)) if c != '0')

def solve(n):
    result = min(map(partial(check, str(n)[::-1]), ('00', '25', '50', '75')))
    return -1 if result == float('inf') else result

from collections import Counter as C
def solve(n):
    c, l, li = C(str(n)), len(str(n))-1, []
    for i in '75 50 00 25'.split():
        if not C(i) - c:
            a, b, cn, t = i[0], i[1], 0, list(str(n))
            bi = l - t[::-1].index(b)
            t.insert(l, t.pop(bi))
            ai = l - t[:-1][::-1].index(a) - 1
            t.insert(l-1, t.pop(ai))
            li.append((l-bi)+(l-ai-1)+[0,next(k for k,l in enumerate(t) if l!='0')][t[0]=='0'])
    return min(li,default=-1)   #headache

def solve(n):
    sn = str(n)
    ret = 0
    if n % 25 == 0:
        return ret
    
    if not (sn.count('0') >= 2 or ('5' in sn and ('0' in sn or '2' in sn or '7' in sn))):
        return -1

    zeroes = []
    two = -1
    five = -1
    seven = -1
    for c in range(len(sn)):
        if sn[c] == '0':
            zeroes.append(c)
        elif sn[c] == '2':
            two = c
        elif sn[c] == '5':
            five = c
        elif sn[c] == '7':
            seven = c
            
    minlen = 2 * len(sn)
    if len(zeroes) > 1:
        # '00' possible
        # move last 0 to last place
        steps = len(sn) - zeroes[-1] - 1
        # move second to last 0 to last place but 1
        steps += len(sn) - zeroes[-2] - 2
        minlen = min(minlen, steps)
    
    if five != -1:
        # 25
        if two != -1:
            steps = 2*len(sn) - five - two - 3
            if five < two:
                steps += 1
            # check for leading zeroes
            min25 = min(two,five)
            max25 = max(two,five)
            popsn = sn[:min25] + sn[min25+1:max25] + sn[max25+1:]
            update = False
            while len(popsn) > 0 and popsn[0] == '0':
                steps += 1
                update = True
                popsn = popsn[1:]
            minlen = min(minlen, steps)
        # 50
        if zeroes != []:
            steps = 2*len(sn) - five - zeroes[-1] - 3
            if zeroes[-1] < five:
                steps += 1
            # check for leading zeroes
            min50 = min(zeroes[-1],five)
            max50 = max(zeroes[-1],five)
            popsn = sn[:min50] + sn[min50+1:max50] + sn[max50+1:]
            update = False
            while len(popsn) > 0 and popsn[0] == '0':
                steps += 1
                update = True
                popsn = popsn[1:]
            minlen = min(minlen, steps)
        # 75
        if seven != -1:
            steps = 2*len(sn) - five - seven - 3
            if five < seven:
                steps += 1
            # check for leading zeroes
            min75 = min(five,seven)
            max75 = max(five,seven)
            popsn = sn[:min75] + sn[min75+1:max75] + sn[max75+1:]
            update = False
            while len(popsn) > 0 and popsn[0] == '0':
                steps += 1
                update = True
                popsn = popsn[1:]
            minlen = min(minlen, steps)
    return minlen


def spoc(sn, cyfry):
    if sn.endswith(cyfry): return 0
    cp, co = cyfry
    res = 0
    ico = sn.rfind(co)
    res += len(sn) - ico - 1
    sn = sn[:ico] + sn[ico+1:]
    icp = sn.rfind(cp)
    res += len(sn) - icp - 1
    sn = sn[:icp] + sn[icp+1:]
    lpz,i  = 0, 0
    if len(sn) > 0 and len(sn) == sn.count('0'): return None
    while sn and sn[i] == '0':
        i += 1
        lpz += 1
    return res + lpz
    
def solve(n):
    print(n)
    sn, t = str(n), []
    if sn.count('0') > 1:
        t.append(spoc(sn, '00'))
    for p, d in ('25', '50', '75'):
        if p in sn and d in sn:
            x = spoc(sn, p + d)
            if x is not None: t.append(x)
    return min(t) if t else -1


import re
def difference(n,s) :
    n = str(n)[::-1]
    try :
        right_index = n.index(s[1])
        left_index = n.index(s[0],[0,1 + right_index][s[0] == s[1]])
    except : return float('inf')
    upper_index = 1 + max(left_index,right_index) == len(n) and '0' == n[-2] and len(re.search('0*.$',n)[0])
    return right_index + left_index - 1 + (left_index < right_index) + (upper_index and upper_index - 1 - (len(n) <= min(left_index,right_index) + upper_index))
def solve(n) :
    removes = min([difference(n,'00'),difference(n,'25'),difference(n,'50'),difference(n,'75')])
    return [-1,removes][int == type(removes)]


def solve(n):
    min_cost = -1
    # Change the number to a string and reverse it.
    # This makes it possible to use .index to find the lowest place a digit appears.
    s = str(n)[::-1]
    # Calculate cost of each possible ending digit pair and keep the lowest cost at each point.
    for pair in ('00', '25', '50', '75'):
        cost = calculate_ending_pair_cost(s, pair)
        if min_cost == -1 or (cost != -1 and cost < min_cost):
            min_cost = cost

    return min_cost

def calculate_ending_pair_cost(s, pair):
    # First find the lowest place indices of each desired digit.
    indices = find_lowest_places(s, pair)
    # If we can't find one of the digits, then this ending pair is not possible.
    if indices is None:
        return -1
    # Start with an indicator of whether the desired digits are in the wrong order.
    cost = 1 if indices[1] > indices[0] else 0
    # Then swap the digits so the rest of our calculations are easier.
    # It's important to have these index numbers in descending order.
    if cost == 1:
        indices = (indices[1], indices[0])
    # Our number now looks something like this: aaa2bbbb5ccccc
    # The cost should be the number of digits (b's) we have to displace to move the 2 next to the 5,
    # and then twice the number of digits to displace to move the 2 and 5 to the "front".
    # This is because each c must move past both the 5 and the 2.
    cost += indices[0] - indices[1] - 1
    cost += 2 * indices[1]
    # We have one last special case to consider: What about leaving leading 0's in the high places?
    # We only bother with this if the number is more than 2 digits.
    if len(s) > 2:
        non_zero_index = first_non_zero_digit_after_indices(s, indices)
        if non_zero_index is None:
            return -1
        cost += len(s) - 1 - non_zero_index
        # Make sure not to double-count any digits we already moved as part of the desired digits.
        cost -= 1 if indices[0] > non_zero_index else 0
        cost -= 1 if indices[1] > non_zero_index else 0
    
    return cost

def find_lowest_places(s, pair):
    # Split the two-char string into the 1s place digit and 10s place digit.
    d10, d1 = pair
    try:
        i1 = s.index(d1)
    except:
        # Index raises an exception if it can't find the digit,
        # so just use that as an indicator of when to return a failure state.
        return None
    
    # If the pair we're looking for is a repeated digit ('00'), then we need to start
    # searching the string at the index just beyond that point.
    start = 0
    if d1 == d10:
        start = i1 + 1
    try:
        i10 = s.index(d10, start)
    except:
        return None
    
    return (i10, i1)

def first_non_zero_digit_after_indices(s, indices):
    # We need to reverse the string and indices because we're actually searching
    # from the highest place in the number now.
    s = s[::-1]
    indices = [len(s) - 1 - indices[i] for i in range(len(indices))]
    # Now search through linearly for the first non-zero digit which is not an index.
    non_zero_index = None
    for i in range(len(s)):
        if i not in indices and s[i] != '0':
            non_zero_index = i
            break
    # If we didn't find a non-zero index, that means we're dealing with something like
    # trying to use '25' as the final digits of 250. In other words, impossible.
    if non_zero_index is None:
        return None
    # Otherwise, translate the index back into place number.
    return len(s) - 1 - non_zero_index

import re
def solve(n):
    print(n)
    min_moves=[]
    n=str(n)
    l=len(n)-1
    extra=0
    
    if '2' in n and '5' in n:
        print('25')
        temp=list(n)
        if n.rindex('2') > n.rindex('5'):
            extra=1
        temp[n.rindex('2')]=''
        temp[n.rindex('5')]=''
        #temp=re.sub(' ','',temp)
        temp=''.join(temp)
        if temp and temp[0]=='0':
                x = re.search("[1-9]", temp)
                if x:
                    extra+=x.start()
        min_moves.append(l-1-n.rindex('2')+l-n.rindex('5')+extra)
        extra=0
         
    if '7' in n and '5' in n:
        print('75')
        temp=list(n)
        if n.rindex('7') > n.rindex('5'):
            extra=1
        temp[n.rindex('7')]=''
        temp[n.rindex('5')]=''
        #temp=re.sub(' ','',temp)
        temp=''.join(temp)
        print('temp',temp)
        if temp and temp[0]=='0':
                x = re.search("[1-9]", temp)
                if x:
                    extra+=x.start()
                    print('extra',extra)
        min_moves.append(l-1-n.rindex('7')+l-n.rindex('5')+extra)
        extra=0
    if '5' in n and '0' in n:
        print('50')
        if n.rindex('5') > n.rindex('0'):
            extra=1
        
        min_moves.append(l-1-n.rindex('5')+l-n.rindex('0')+extra)
        extra=0
    if n.count('0')>1:
        print('00')
        min_moves.append(l-1-n.rindex('0')+l-n.rindex('0',0,n.rindex('0')))
    print(min_moves)
    if not min_moves:
        return -1
    return min(min_moves)