Algorithm 查找2个字符串的所有交替连接
因此,我试图编写一个函数来查找两个字符串的所有交替连接。如果说Algorithm 查找2个字符串的所有交替连接,algorithm,concatenation,dynamic-programming,combinatorics,Algorithm,Concatenation,Dynamic Programming,Combinatorics,因此,我试图编写一个函数来查找两个字符串的所有交替连接。如果说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 == ""
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)]
我怎样才能避免重复?(我不想检查是否已经添加了元素;我对生成唯一序列的“真正”方法感兴趣)这最好使用递归方法。去掉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 )
复制品从哪里来?
代码两次构建t
的子字符串:通过在j
的值上循环,并始终在每个递归上选择s
的空前缀
对于j
的每次迭代,您将追加t[:j]
,然后追加一个以i=0
开头的递归调用,从而追加更多的t
字符。因此,来自t
的相同字符子串将由j
循环和递归创建。例如,“12ab”可以通过在第一级递归中以“1”开头,或者通过在第一层递归中以“12”开头来构建
(字符串越长,重复的模式越明显,比如说“abc”和“123”。)
修复原始解决方案
让我们修复原始解决方案。我们希望通过j
循环或递归来构建t
的每个附加子字符串。为了好玩,我将展示每个问题的解决方案
首先,让我们保持j
循环。这意味着我们需要强制对g
的每个递归调用不以t
字符开头。但是,我们还需要对g
进行特殊的case-first调用,以生成以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
的前缀,因此将构建大于1的t
子字符串
另外,当递归工作时,您可以使用
for i in xrange( len(s) + 1) :
排除线路
res.append( s + t )
您正在从0…len(a)+len(b)-1中选择len(b)索引,以便b的字符显示在其中(其他索引从a获取字符)。这建议使用itertools.compositions
,它提供了如下解决方案:
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'))
顺便提一下,我在Mathematica.SE上也提出了同样的问题(虽然不是针对字符串)。我的解决方案使用了五角大楼描述的算法:
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}}
使用:
此代码几乎完全使用Mathematica的代码,唯一的例外是。谢谢,
yield
行让我很开心。你真的不需要映射到set()
,是吗?从功能上讲,set()是多余的,但由于它使i中的xO(1)而不是O(len(b)
),它会被调用len(a)+len(b)
。
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}}