Find all list permutations of splitting a string in Python

后端 未结 6 2277
迷失自我
迷失自我 2020-12-30 06:42

I have a string of letters that I\'d like to split into all possible combinations (the order of letters must be remain fixed), so that:

s = \'monkey\'


        
相关标签:
6条回答
  • 2020-12-30 06:43

    Consider more_itertools.partitions:

    Given

    import more_itertools as mit
    
    
    s = "monkey"
    

    Demo

    As-is:

    list(mit.partitions(s))
    #[[['m', 'o', 'n', 'k', 'e', 'y']],
    # [['m'], ['o', 'n', 'k', 'e', 'y']],
    # [['m', 'o'], ['n', 'k', 'e', 'y']],
    # [['m', 'o', 'n'], ['k', 'e', 'y']],
    # [['m', 'o', 'n', 'k'], ['e', 'y']],
    # [['m', 'o', 'n', 'k', 'e'], ['y']],
    # ...]
    

    After some string joining:

    [list(map("".join, x)) for x in mit.partitions(s)]
    

    Output

    [['monkey'],
     ['m', 'onkey'],
     ['mo', 'nkey'],
     ['mon', 'key'],
     ['monk', 'ey'],
     ['monke', 'y'],
     ['m', 'o', 'nkey'],
     ['m', 'on', 'key'],
     ['m', 'onk', 'ey'],
     ['m', 'onke', 'y'],
     ['mo', 'n', 'key'],
     ['mo', 'nk', 'ey'],
     ['mo', 'nke', 'y'],
     ['mon', 'k', 'ey'],
     ['mon', 'ke', 'y'],
     ['monk', 'e', 'y'],
     ['m', 'o', 'n', 'key'],
     ['m', 'o', 'nk', 'ey'],
     ['m', 'o', 'nke', 'y'],
     ['m', 'on', 'k', 'ey'],
     ['m', 'on', 'ke', 'y'],
     ['m', 'onk', 'e', 'y'],
     ['mo', 'n', 'k', 'ey'],
     ['mo', 'n', 'ke', 'y'],
     ['mo', 'nk', 'e', 'y'],
     ['mon', 'k', 'e', 'y'],
     ['m', 'o', 'n', 'k', 'ey'],
     ['m', 'o', 'n', 'ke', 'y'],
     ['m', 'o', 'nk', 'e', 'y'],
     ['m', 'on', 'k', 'e', 'y'],
     ['mo', 'n', 'k', 'e', 'y'],
     ['m', 'o', 'n', 'k', 'e', 'y']]
    

    Install via > pip install more_itertools.

    0 讨论(0)
  • 2020-12-30 06:45

    http://wordaligned.org/articles/partitioning-with-python contains an interesting post about sequence partitioning, here is the implementation they use:

    #!/usr/bin/env python
    
    # From http://wordaligned.org/articles/partitioning-with-python
    
    from itertools import chain, combinations
    
    def sliceable(xs):
        '''Return a sliceable version of the iterable xs.'''
        try:
            xs[:0]
            return xs
        except TypeError:
            return tuple(xs)
    
    def partition(iterable):
        s = sliceable(iterable)
        n = len(s)
        b, mid, e = [0], list(range(1, n)), [n]
        getslice = s.__getitem__
        splits = (d for i in range(n) for d in combinations(mid, i))
        return [[s[sl] for sl in map(slice, chain(b, d), chain(d, e))]
                for d in splits]
    
    if __name__ == '__main__':
        s = "monkey"
        for i in partition(s):
            print i
    

    Which would print:

    ['monkey']
    ['m', 'onkey']
    ['mo', 'nkey']
    ['mon', 'key']
    ['monk', 'ey']
    ['monke', 'y']
    ['m', 'o', 'nkey']
    ['m', 'on', 'key']
    ['m', 'onk', 'ey']
    ['m', 'onke', 'y']
    ['mo', 'n', 'key']
    ['mo', 'nk', 'ey']
    ['mo', 'nke', 'y']
    ['mon', 'k', 'ey']
    ['mon', 'ke', 'y']
    ['monk', 'e', 'y']
    ...
    ['mo', 'n', 'k', 'e', 'y']
    ['m', 'o', 'n', 'k', 'e', 'y']
    
    0 讨论(0)
  • 2020-12-30 06:47

    The idea is to realize that the permutation of a string s is equal to a set containing s itself, and a set union of each substring X of s with the permutation of s\X. For example, permute('key'):

    1. {'key'} # 'key' itself
    2. {'k', 'ey'} # substring 'k' union 1st permutation of 'ey' = {'e, 'y'}
    3. {'k', 'e', 'y'} # substring 'k' union 2nd permutation of 'ey' = {'ey'}
    4. {'ke', 'y'} # substring 'ke' union 1st and only permutation of 'y' = {'y'}
    5. Union of 1, 2, 3, and 4, yield all permutations of the string key.

    With this in mind, a simple algorithm can be implemented:

    >>> def permute(s):
        result = [[s]]
        for i in range(1, len(s)):
            first = [s[:i]]
            rest = s[i:]
            for p in permute(rest):
                result.append(first + p)
        return result
    
    >>> for p in permute('monkey'):
            print(p)    
    
    ['monkey']
    ['m', 'onkey']
    ['m', 'o', 'nkey']
    ['m', 'o', 'n', 'key']
    ['m', 'o', 'n', 'k', 'ey']
    ['m', 'o', 'n', 'k', 'e', 'y']
    ['m', 'o', 'n', 'ke', 'y']
    ['m', 'o', 'nk', 'ey']
    ['m', 'o', 'nk', 'e', 'y']
    ['m', 'o', 'nke', 'y']
    ['m', 'on', 'key']
    ['m', 'on', 'k', 'ey']
    ['m', 'on', 'k', 'e', 'y']
    ['m', 'on', 'ke', 'y']
    ['m', 'onk', 'ey']
    ['m', 'onk', 'e', 'y']
    ['m', 'onke', 'y']
    ['mo', 'nkey']
    ['mo', 'n', 'key']
    ['mo', 'n', 'k', 'ey']
    ['mo', 'n', 'k', 'e', 'y']
    ['mo', 'n', 'ke', 'y']
    ['mo', 'nk', 'ey']
    ['mo', 'nk', 'e', 'y']
    ['mo', 'nke', 'y']
    ['mon', 'key']
    ['mon', 'k', 'ey']
    ['mon', 'k', 'e', 'y']
    ['mon', 'ke', 'y']
    ['monk', 'ey']
    ['monk', 'e', 'y']
    ['monke', 'y']
    
    0 讨论(0)
  • 2020-12-30 06:55

    My solution allows you to also set a threshold for the minimum size of sub string

    This is my code:

    def split_string (s, min_str_length = 2, root_string=[], results=[] ):
        """
        :param s: word to split, string
        :param min_str_length: the minimum character for a sub string
        :param root_string:  leave empty
        :param results: leave empty
        :return: nested list of all possible combinations of word split according to the minimum substring length
        """
        for i in range(min_str_length,len(s)):
            if i == min_str_length:
                primary_root_string=root_string
            else:
                root_string = primary_root_string
            if len(s[i:])>= min_str_length :
                results.append(list(chain(*[root_string,[s[:i]],[s[i:]]])))
                root_string = list(chain(*[root_string,[s[:i]]]))
                split_string(s[i:], min_str_length, root_string, results)
        return results
    

    Examples of use:

    Input: split_string ('monkey', min_str_length = 1, root_string=[], results=[] )
    Output: 
    [['m', 'onkey'],
     ['m', 'o', 'nkey'],
     ['m', 'o', 'n', 'key'],
     ['m', 'o', 'n', 'k', 'ey'],
     ['m', 'o', 'n', 'k', 'e', 'y'],
     ['m', 'o', 'n', 'ke', 'y'],
     ['m', 'o', 'nk', 'ey'],
     ['m', 'o', 'nk', 'e', 'y'],
     ['m', 'o', 'nke', 'y'],
     ['m', 'on', 'key'],
     ['m', 'on', 'k', 'ey'],
     ['m', 'on', 'k', 'e', 'y'],
     ['m', 'on', 'ke', 'y'],
     ['m', 'onk', 'ey'],
     ['m', 'onk', 'e', 'y'],
     ['m', 'onke', 'y'],
     ['mo', 'nkey'],
     ['mo', 'n', 'key'],
     ['mo', 'n', 'k', 'ey'],
     ['mo', 'n', 'k', 'e', 'y'],
     ['mo', 'n', 'ke', 'y'],
     ['mo', 'nk', 'ey'],
     ['mo', 'nk', 'e', 'y'],
     ['mo', 'nke', 'y'],
     ['mon', 'key'],
     ['mon', 'k', 'ey'],
     ['mon', 'k', 'e', 'y'],
     ['mon', 'ke', 'y'],
     ['monk', 'ey'],
     ['monk', 'e', 'y'],
     ['monke', 'y']]
    

    or

    Input: split_string ('monkey', min_str_length = 2, root_string=[], results=[] )
    Output: [['mo', 'nkey'], ['mo', 'nk', 'ey'], ['mon', 'key'], ['monk', 'ey']]
    
    
    0 讨论(0)
  • 2020-12-30 07:05

    A string (as opposed to list) oriented approach is to think of the each adjacent pair of characters being separated by either a space or empty string. That can be mapped to 1 and 0, and the number of possible splits are a power of 2:

    2 ^ (len(s)-1)

    for example, "key" can have '' or ' ' separating 'ke' and a '' or ' ' separating 'ey' which leads to 4 possibilities:

    • key ('' between 'k' and 'e', '' between 'e' and 'y')
    • k ey (' ' between 'k' and 'e', '' between 'e' and 'y')
    • k e y (' ' between 'k' and 'e', ' ' between 'e' and 'y')
    • ke y ('' between 'k' and 'e', ' ' between 'e' and 'y')

    An unreadable python one liner that gives you a generator in string form:

    operator_positions = (''.join([str(a >> i & 1).replace('0', '').replace('1', ' ') + s[len(s)-1-i] for i in range(len(s)-1, -1, -1)]) for a in range(pow(2, len(s)-1)))
    

    A readable version of this generator with comments and sample:

    s = 'monkey'
    s_length = len(s)-1  # represents the number of ' ' or '' that can split digits
    
    operator_positions = (
        ''.join(
            [str(a >> i & 1).replace('0', '').replace('1', ' ') + s[s_length-i]
             for i in range(s_length, -1, -1)])   # extra digit is for blank string to always precede first digit
        for a in range(pow(2, s_length))   # binary number loop
    )
    for i in operator_positions:
        print i
    

    str(a >> i & 1) converts a into a binary string, which then has it's 0's and 1's replaced by '' and ' ', respectively. The binary string is an extra digit long so that the first digit is always ''. That way, as the digit splitter is combined with the first character, it always results in just the first character.

    0 讨论(0)
  • 2020-12-30 07:08
    def splitter(str):
        for i in range(1, len(str)):
            start = str[0:i]
            end = str[i:]
            yield (start, end)
            for split in splitter(end):
                result = [start]
                result.extend(split)
                yield result
    
    combinations = list(splitter(str))
    

    Note that I defaulted to a generator to save you from running out of memory with long strings.

    0 讨论(0)
提交回复
热议问题