This kata is  blatantly copied from  inspired by This Kata
Welcome

this is the second in the series of the string iterations kata!

Here we go!

---------------------------------------------------------------------------------

We have a string s

Let's say you start with this: "String"

The first thing you do is reverse it: "gnirtS"

Then you will take the string from the 1st position and reverse it again: "gStrin"

Then you will take the string from the 2nd position and reverse it again: "gSnirt"

Then you will take the string from the 3rd position and reverse it again: "gSntri"

Continue this pattern until you have done every single position, and then you will return the string you have created. For this particular string, you would return: 
"gSntir"

now,

The Task:

In this kata, we also have a number x

take that reversal function, and apply it to the string x times.


return the result of the string after applying the reversal function to it x times.

    example where s = "String" and x = 3:

    after 0 iteration  s = "String"
    after 1 iteration  s = "gSntir"
    after 2 iterations s = "rgiStn"
    after 3 iterations s = "nrtgSi"
    
    so you would return "nrtgSi".

 Note 

String lengths may exceed 2 million


x exceeds a billion


be read to optimize



if this is too hard, go here https://www.codewars.com/kata/string-%3E-n-iterations-%3E-string/java

Difficulty: interview

Starter code:

def string_func(s, n):
	

Solutions:
def string_func(s, n):
    l, s = [s], list(s)
    while True:
        s[::2], s[1::2] = s[:len(s)//2-1:-1], s[:len(s)//2]
        l.append(''.join(s))
        if l[0] == l[-1]: del l[-1]; break
    return l[n % len(l)]

# SOLUTION:
# Actually doing the reversals repeatedly is slow. Through observeration
# we can find a mapping of single shifts which take the original string directly
# to the target string while saving a lot of computation. The resulting "optimized"
# manipulations will be different for strings of different lengths,
# but the same for all strings of a given length.
#
# Over the course of multiple sets of reversals, each character necessarily goes through a loop.
# For example, if the original string is '1234', then the character at index 0
# will first move to index 1, then after another set of reversals to index 3,
# and then back to index 0, thus completing a loop.
# Index 2 may always stay in place.
# For longer strings, the mapping often results in having multiple of these loops.
#
# Once the loops are mapped out, we can easily determine where each character will end up
# after applying the reversal function x times.

def string_func(in_str, action_times):
    
    input_len = len(in_str)
    input_mid_point = (input_len + 1) / 2 - 1

    # First we'll map out the loops.
    # Starting from index 0, figure out where each character will go after one set of reversals.

    loops = [[0]]
    index = 0

    # Keep track of which loop each character starts in, so that when we want to place it in our new string we'll know where to look.
    # Right now they're all set to 0, but that will change as we check each index.
    loop_index_is_in = [0]*input_len

    indices_not_checked = list(range(1, input_len))

    while len(indices_not_checked) != 0:

        # Next-step transitions can be easily calculated
        if index < input_mid_point:
            index = index * 2 + 1
        elif index > input_mid_point:
            index = (input_len - 1 - index)*2
        elif index == input_mid_point:
            index = input_len - 1

        # Next, we'll need to find each of the next locations (the "loop") for that character.

        # If we return to an index we've already checked, it means that we've completed a loop and we're done with that start index.
        # In that case, we'll add a new empty loop to loops and switch to checking an index that hasn't been checked yet,
        # until we've mapped out all of the loops.
        if index not in indices_not_checked:
            loops.append([])
            index = indices_not_checked[0]

        # Adding the index to the loop it belongs in (the loop we are building is always the last one in loops,
        # as any earlier loops we've already completed.)
        loops[-1].append(index)

        indices_not_checked.remove(index)

        # And we'll keep track of which loop each character starts in,
        # so that when we want to place it in our final result string we'll know where to look.
        loop_index_is_in[index] = len(loops) - 1


    # Now that we mapped out the loops, we need to find which index each character will end up at.

    # For now the final result string (new_string) is actually a list, so that we can use item assignment.
    new_string = [0] * input_len

    # For each character in the origional string:
    for char_index in range(input_len):

        # Find the loop that it is in:
        loop = loops[loop_index_is_in[char_index]]

        # Find where it is in that loop
        place_in_loop = loop.index(char_index)

        # Figure out where it will go (where it is in the loop, plus the amount of
        # times we want to apply the reversal function, which is the amount of 'steps' the character will take, mod the length of the loop)
        new_index = loop[(place_in_loop + action_times)%len(loop)]

        # Insert the character in its place in new_string
        new_string[new_index] = in_str[char_index]

    # After placing each character in the new_string list, convert the list to a string and return it. That's our final result string.
    return ''.join(new_string)


def string_func(s, x):
    if not x: return s
    k = [s]

    new_s = list(s)
    while True:
    
        #shufling!
        new_s[::2], new_s[1::2] = new_s[:len(new_s)//2-1:-1], new_s[:len(new_s)//2]
        
        k.append(''.join(new_s))
        if k[-1] == s:
            return k[x%(len(k)-1)]
    



from itertools import chain, islice
from functools import lru_cache

def reversal_func(s):
    return ''.join(islice(chain.from_iterable(zip(s[::-1], s)), len(s)))

@lru_cache()
def period(n):
    # see https://oeis.org/A216066 , comment by [Robert Pfister, Sep 12 2013]
    # and https://oeis.org/A003558 , formula by [Jonathan Skowera, Jun 29 2013]
    last = 1
    for i in range(n):
        val = abs(2 * n + 1 - 2 * last)
        if val == 1:
            return i + 1
        last = val

def string_func(s,x):
    x %= period(len(s))
    for __ in range(x):
        s = reversal_func(s)
    return s

def string_func(s, n):
    m, table = len(s), {}
    for i in range(m):
        if i not in table:
            j, cycle = i, []
            while i != j or not cycle:
                cycle.append(j)
                j = min(2 * j + 1, 2 * (m - j - 1))
            table.update(zip(cycle[n % len(cycle):] + cycle, cycle))
    return ''.join(s[table[i]] for i in range(m))

def exp(permutation, n):
    result = [-1] * len(permutation)
    for i in range(len(permutation)):
        if result[i] == -1:
            j, cycle = i, []
            while True:
                cycle.append(j)
                j = permutation[j]
                if i == j: break
            for j, k in enumerate(cycle):
                result[k] = cycle[(j + n) % len(cycle)]
    return result
    
def string_func(s, n):
    m = len(s)
    perm = [i // 2 if i % 2 else m - 1 - i // 2 for i in range(m)]
    return ''.join(s[i] for i in exp(perm, n))

import copy


def string_func(s,x):
    m,l,arr,z = 0,len(s),[0]*len(s),0
    while True:
        m += 1
        y = (2**m) % (2*l+1)
        if y == 1 or y == 2*l: break
    for i in range(1,l,2): 
        arr[z] = i;
        z += 1
    for i in range(l-2+l%2,-1,-2):
        arr[z] = i
        z += 1
    ss = list(s)
    x %= m
    while x > 0:
        x -= 1
        for i in range(len(arr)):
            ss[arr[i]] = s[i]
        s = ss.copy()
    return ''.join(s)

def string_func(s, x):
    len_s = len(s)
    arr = list(s)
    mapping = []
    correct_seq = list(range(len_s))
    changed_seq = correct_seq[:]

    # Make index mapping
    for i in range(int(len_s / 2 + 0.5)):  # the result of placing the indexes
        mapping.append(len_s - i - 1)      # of the once applied "reversal function"
        mapping.append(i)
    if len_s % 2 != 0:
        mapping.pop()

    # Reduce x
    for div in range(1, len_s + 1):                      # take a remainder of the division
        changed_seq = [changed_seq[i] for i in mapping]  # by the distance between repetitions
        if changed_seq == correct_seq:
            x %= div
            break

    while x:
        arr = [arr[i] for i in mapping]
        x -= 1

    return ''.join(arr)

def string_func(s, n):
    L2 = len(s) // 2
    l = [s]
    s = list(s)
    while True:
        s[0::2], s[1::2] = s[:L2-1:-1], s[:L2]
        l.append(''.join(s))
        if l[0] == l[-1]:
            del l[-1]
            break
    return l[n % len(l)]

def string_func(s, x):
    l = len(s)
    cyc = x % life(l)
    order = list(s)
    string = list(s)
    
    c = 0
    for i in range(l-1,-1+l//2,-1): 
        order[c] = i
        c += 2
    c = 1
    for i in range(0,l//2,1):
        order[c] = i
        c += 2

    return "".join(sort(order, string, cyc))

def sort(schema, string, times):
    for x in range(times):
        result = []
        for i in range(len(string)):
            result.append(string[schema[i]])
        string = result
    return string

def life(n):
    if n <= 1:
        return 1
    m = 1
    while True:
        a = (2 ** m) % (2 * n + 1)
        if a == 1 or a == 2 * n:
            return m
        m = m + 1