我正在尝试生成所有可能的方法来在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')
但正如预期的那样,这会返回所有可能的排列,而忽略a
和b
(以及c
和d
)的顺序。
答案 0 :(得分:16)
让您想要交错的两个字符串为s
和t
。我们将使用递归来生成交错这两个字符串的所有可能方法。
如果我们在任何时候交错了i
的{{1}}个字符和s
的第一个j
字符,就会创建一些字符串t
,然后我们有两种方法将它们交错用于下一步 -
res
的{{1}}字符附加到i+1
s
的{{1}}字符附加到res
我们继续这种递归,直到两个字符串的所有字符都被使用,然后我们将这个结果存储在字符串列表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)
您只需将a
与b
和c
的索引与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。
没问题。我们可以将这种方法归结为这种可憎的行为:
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))})
(对结果使用集合理解而不是更早地确保唯一性。)