交错两个字符串的所有可能方法

时间:2016-03-28 11:01:15

标签: python permutation itertools

我正在尝试生成所有可能的方法来在Python中交错任意两个任意字符串。

例如:如果两个字符串是'ab''cd',我希望获得的输出是:

['abcd', 'acbd', 'acdb', 'cabd', 'cadb', 'cdab']

a始终在b之前(c之前d)。我正在努力寻找解决方案。我已经尝试了如下所示的itertools:

import itertools

def shuffle(s,t):
    string = s+t
    for i in itertools.permutations(string):
        print(''.join(i))

shuffle('ab','cd')

但正如预期的那样,这会返回所有可能的排列,而忽略ab(以及cd)的顺序。

5 个答案:

答案 0 :(得分:16)

想法

让您想要交错的两个字符串为st。我们将使用递归来生成交错这两个字符串的所有可能方法。

如果我们在任何时候交错了i的{​​{1}}个字符和s的第一个j字符,就会创建一些字符串t ,然后我们有两种方法将它们交错用于下一步 -

  1. res的{​​{1}}字符附加到i+1
  2. s的{​​{1}}字符附加到res
  3. 我们继续这种递归,直到两个字符串的所有字符都被使用,然后我们将这个结果存储在字符串列表j+1中,如下面的代码所示。

    守则

    t

    输出

    res

    这个实现和我们可以获得的效率一样高效(至少渐近),因为我们从不会两次生成相同的字符串。

答案 1 :(得分:13)

已经发布了其他几个解决方案,但是大多数解决方案都会在内存中生成交错字符串(或其等价物)的完整列表,从而使其内存使用量随输入长度呈指数增长。当然必须有更好的方法。

枚举所有交错两个序列的方法,长度分别为 a b ,与枚举所有 a + b 位整数,设置了 b 位。每个这样的整数对应于交错序列的不同方式,通过用第一序列的元素替换每0位获得,并且每1位用第二序列的元素替换。

方便地,calculate the next integer with the same number of bits set有一种聪明而有效的方法,我们可以使用它来生成所有这样的整数。我们首先要做的是:

def bit_patterns(m, n):
    """Generate all m-bit numbers with exactly n bits set, in ascending order.
    See http://www.geeksforgeeks.org/next-higher-number-with-same-number-of-set-bits/
    """
    patt = (1 << int(n)) - 1
    if patt == 0: yield 0; return  # loop below assumes patt has at least one bit set!
    while (patt >> m) == 0:
        yield patt
        lowb = patt & -patt  # extract the lowest bit of the pattern
        incr = patt + lowb   # increment the lowest bit
        diff = patt ^ incr   # extract the bits flipped by the increment
        patt = incr + ((diff // lowb) >> 2)  # restore bit count after increment

现在我们可以使用这个生成器生成交错任意两个序列的所有方法:

def interleave(a, b):
    """Generate all possible ways to interleave two sequences."""
    m = len(a) + len(b)
    n = len(a)
    for pattern in bit_patterns(m, n):
        seq = []
        i = j = 0
        for k in range(m):
            bit = pattern & 1
            pattern >>= 1
            seq.append(a[i] if bit else b[j])
            i += bit
            j += 1-bit
        yield seq

请注意,为了尝试尽可能通用,此代码采用任意序列类型并返回列表。字符串是Python中的序列,因此您可以很好地传递它们;要将生成的列表转换回字符串,您可以连接它们的元素,例如使用"".join(),就像这样:

foo = "ABCD"
bar = "1234"
for seq in interleave(foo, bar):
    print("".join(seq))

我们走了:一个完全非递归有效的基于发生器的解决方案,即使对于长输入也只使用很少的内存,并且只生成一次输出(因此不需要低效的重复消除步骤)。它甚至可以在Python 2和3中使用。

答案 2 :(得分:9)

效率极低但工作正常:

def shuffle(s,t):
    if s=="":
        return [t]
    elif t=="":
        return [s]
    else:
        leftShuffle=[s[0]+val for val in shuffle(s[1:],t)]
        rightShuffle=[t[0]+val for val in shuffle(s,t[1:])]
        leftShuffle.extend(rightShuffle)
        return leftShuffle

print(shuffle("ab","cd"))

答案 3 :(得分:4)

您只需将abc的索引与d进行比较,然后过滤出a的索引大于的b c的索引和d的索引大于def interleave(s, t): mystring = s + t return [el for el in [''.join(item) for item in permutations(mystring) if item.index('a') < item.index('b') and item.index('c') < item.index('d')]] 的索引。

>>> from itertools import permutations
>>> s = 'ab'
>>> t = 'cd'
>>> [el for  el in [''.join(item) for item in permutations(s+t) if item.index('a') < item.index('b') and item.index('c') < item.index('d')]]
['abcd', 'acbd', 'acdb', 'cabd', 'cadb', 'cdab']

演示:

public ActionResult UserDetails(DashBoard dash)
    {            
        using (var cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
        {
            String sql = "SELECT DU.UserRMN, MU.Name, DeptName, MgrID, TLeadID FROM Details_Users DU, Master_Users MU where DU.UserRMN=MU.UserRMN";
            SqlCommand cmd = new SqlCommand(sql, cn);           
            cn.Open();
            SqlDataReader rdr = cmd.ExecuteReader();
            List<DashBoard> model = new List<DashBoard>();
            while(rdr.Read())
            {
                var details = new DashBoard();
                details.UserRMN = rdr["UserRMN"].ToString();
                details.Name = rdr["Name"].ToString();
                details.DeptID = rdr["DeptID"].ToString();
                details.MgrID = rdr["MgrID"].ToString();
                details.TLeadID = rdr["TLeadID"].ToString();
                model.Add(details);
            }
            return View(model);
        }  
    }

答案 4 :(得分:2)

仅限运动

没有显式条件或谓词的解决方案

(即没有任何if个关键字):

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)

我们需要一个帮助函数:

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)))

如果非唯一排列的数量很大,由于调用sorted,这可能效率很低。有关获取非唯一值的唯一排列的替代方法,请参阅permutations with unique values

TL; DR

没问题。我们可以将这种方法归结为这种可憎的行为:

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))})

(对结果使用集合理解而不是更早地确保唯一性。)