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中的
x
O(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}}