所以我正在尝试编写一个函数来查找2个字符串的所有备用连接。说出x = '12'
和y = 'ab'
,然后预期结果为['ab12', 'a1b2', 'a12b', '1ab2', '1a2b', '12ab']
。我写了以下程序:
# [ list of sequences where each seq is a concat of s and t ]
def g( s, t ) :
#
if ( s == "" ) :
return [ t ]
elif ( t == "" ) :
return [ s ]
else :
res = []
for i in xrange( len(s) ) :
for j in xrange( 1, len(t) + 1 ) :
res.extend( [ s[:i] + t[:j] + x for x in g( s[i:], t[j:] ) ] )
#
res.append( s + t )
return res
它输出正确的结果,但有些序列有重复:
In [22]: r = g( "12", "ab" )
[ (x, r.count(x)) for x in set( r ) ]
Out[22]: [('ab12', 2), ('12ab', 1), ('1ab2', 2), ('a12b', 1), ('1a2b', 1), ('a1b2', 1)]
如何避免重复? (我不想检查是否已经添加了一个元素;我对一个"真正的"生成独特序列的方式感兴趣)
答案 0 :(得分:1)
最好使用递归方法。剥离s中的第一个字符,找到剩余字符串的所有组合,并对t执行相同的操作:
def g1( s, t ):
return [s[0]+x for x in g( s[1:], t )]
def g(s,t):
if( s=="" ):
return [t]
elif t=="":
return [s]
else:
return g1( s, t ) + g1( t, s )
答案 1 :(得分:0)
代码正在构建t
两次的子字符串:循环遍历j
的值,并始终在每次递归时选择s
的空前缀。
对于j
的每次迭代,您都会附加t[:j]
,然后附加一个以i = 0
开头的递归调用,从而附加更多字符t
。因此,t
循环和递归将创建j
中相同的字符子串。例如,“12ab”可以通过在第一级递归中以“1”开头,或者在第一层递归中以“12”开头来构建。
(重复的模式对于更长的字符串更明显,比如“abc”和“123”。)
让我们修复原始解决方案。我们希望通过t
循环或递归来构建j
的每个附加子串。为了好玩,我会为每个人展示一个解决方案。
首先,让我们保持j
循环。这意味着我们需要强制每次递归调用g
,而不是以t
的字符开头。但是我们还需要第一次调用g
来生成以t
字符开头的字符串。这是一种半黑客的方式:
def g( s, t, z=0 ) :
if ( s == "" ) :
return [ t ]
elif ( t == "" ) :
return [ s ]
else :
res = []
for i in xrange( z, len(s) ) :
for j in xrange( 1, len(t) + 1 ) :
res.extend( [ s[:i] + t[:j] + x for x in g( s[i:], t[j:], 1) ])
res.append( s + t )
return res
对g
的每次调用都会遍历所有字符串,其前缀为s
,后跟t
的所有前缀,后跟递归(必须以s
开头) 。第一次调用g
是一种特殊情况,因此我们明确允许第一次调用允许t
作为第一个前缀。
或者我们可以让递归构建t
。
def g( s, t, ) :
if ( s == "" ) :
return [ t ]
elif ( t == "" ) :
return [ s ]
else :
res = []
for i in xrange( len(s) ) :
res.extend( [ s[:i] + t[:1] + x for x in g( s[i:], t[1:] ) ] )
res.append( s + t )
return res
在此版本中,我们选择s
的所有前缀,添加t
中的字符,然后递归。由于我们将""
计为s
的前缀,因此将构建t
大于1的子字符串。
另外,当递归有效时,您可以使用
for i in xrange( len(s) + 1) :
消除这条线
res.append( s + t )
答案 2 :(得分:0)
你要从0 ... len(a)+ len(b)-1中选择len(b)索引,以便出现b的字符(其他索引从a获取字符)。这建议使用itertools.combinations
,它提供了这样的解决方案:
import itertools
def concats(a, b):
for i in map(set, itertools.combinations(xrange(len(a) + len(b)), len(b))):
its = iter(a), iter(b)
yield ''.join(next(its[x in i]) for x in xrange(len(a) + len(b)))
print list(concats('abc', '12'))
答案 3 :(得分:0)
顺便说一下,我在Mathematica.SE上recently answered同样的问题(虽然不是字符串)。
我的解决方案使用了十五边形描述的算法:
f[u : {a_, x___}, v : {b_, y___}, c___] := f[{x}, v, c, a] ~Join~ f[u, {y}, c, b]
f[{x___}, {y___}, c___] := {{c, x, y}}
使用:
f[{1, 2}, {a, b}]
{{1, 2, a, b}, {1, a, 2, b}, {1, a, b, 2}, {a, 1, 2, b}, {a, 1, b, 2}, {a, b, 1, 2}}
此代码几乎只使用 Mathematica 的pattern matching syntax,唯一的例外是Join
。