All possible ways to interleave two strings

前端 未结 5 712
傲寒
傲寒 2021-01-03 22:30

I am trying to generate all possible ways to interleave any two arbitrary strings in Python.

For example: If the two strings are \'ab\' and \'cd\'

5条回答
  •  臣服心动
    2021-01-03 23:24

    Just for sports

    a solution without explicit conditionals or predicates

    (i.e., without any if keywords):

    from itertools import chain, repeat, permutations
    from copy import deepcopy
    
    
    def shuffle(*strings):
        # Treat the strings as pools from which to draw elements in order.
        # Convert the strings to lists, so that drawn items can be removed:
        pools = (list(string) for string in strings)
    
        # From each pool, we have to draw as many times as it has items:
        pools_to_draw_from = chain.from_iterable(
            repeat(pool, len(pool)) for pool in pools
        )
    
        # Because itertools.permutations treats elements as unique based on their
        # position, not on their value and because pools_to_draw_from has repeated
        # repeated items, we would get repeated permutations, if we would not
        # filter them out with `unique`.
        possible_drawing_orders = unique(permutations(pools_to_draw_from))
    
        # For each drawing order, we want to draw (and thus remove) items from our
        # pools. Subsequent draws within the same drawing order should get the
        # respective next item in the pool, i.e., see the modified pool. But we don't
        # want the pools to be exhausted after processing the first drawing ordering.
        #
        # Deepcopy preserves internal repetition and thus does exactly what we need.
        possible_drawing_orders = (deepcopy(pdo) for pdo in possible_drawing_orders)
    
        # Draw from the pools for each possible order,
        # build strings and return them in a list:
        return [''.join(_draw(p)) for p in possible_drawing_orders]
    
    
    def _draw(drawing_order):
        return (pool_to_draw_from.pop(0) for pool_to_draw_from in drawing_order)
    

    We need a helper function for this:

    from operator import itemgetter
    from itertools import groupby
    
    def unique(iterable, key=None):
        # Other than unique_everseen from
        # https://docs.python.org/3/library/itertools.html#itertools-recipes, this
        # works for iterables of non-hashable elements, too.
        return unique_justseen(sorted(iterable, key=key), key)
    
    
    def unique_justseen(iterable, key=None):
        """
        List unique elements, preserving order. Remember only the element just seen.
        """
        # from https://docs.python.org/3/library/itertools.html#itertools-recipes
        return map(next, map(itemgetter(1), groupby(iterable, key)))
    

    If the number of non-unique permutations is large, this is probably rather inefficient, due to the call to sorted. For alternatives to obtain unique permutations of non-unique values, see permutations with unique values.

    TL;DR?

    No problem. We can boil this approach down to this abomination:

    from itertools import chain, repeat, permutations
    from copy import deepcopy
    
    def shuffle(*strings):
        return list({''.join(l.pop(0) for l in deepcopy(p)) for p in permutations(chain.from_iterable(repeat(list(s), len(s)) for s in strings))})
    

    (Using a set comprehension on the result instead of ensuring uniqueness earlier.)

提交回复
热议问题