如何在Python中生成一个字符串的所有可能组合,每个字符之间有空格?

7
我该如何生成一个字符串中所有可能的字符组合,并在字符之间添加空格呢?
[in]: "foobar"

[out]: 
['foobar', 'f oobar', 'fo obar', 'f o obar', 'foo bar', 'f oo bar', 'fo o bar', 
'f o o bar', 'foob ar', 'f oob ar', 'fo ob ar', 'f o ob ar', 'foo b ar', 
'f oo b ar', 'fo o b ar', 'f o o b ar', 'fooba r', 'f ooba r', 'fo oba r', 
'f o oba r', 'foo ba r', 'f oo ba r', 'fo o ba r', 'f o o ba r', 'foob a r', 
'f oob a r', 'fo ob a r', 'f o ob a r', 'foo b a r', 'f oo b a r', 'fo o b a r', 
'f o o b a r', 'foobar', 'f oobar', 'fo obar', 'f o obar', 'foo bar', 
'f oo bar', 'fo o bar', 'f o o bar', 'foob ar', 'f oob ar', 'fo ob ar', 
'f o ob ar', 'foo b ar', 'f oo b ar', 'fo o b ar', 'f o o b ar', 'fooba r', 
'f ooba r', 'fo oba r', 'f o oba r', 'foo ba r', 'f oo ba r', 'fo o ba r', 
'f o o ba r', 'foob a r', 'f oob a r', 'fo ob a r', 'f o ob a r', 'foo b a r', 
'f oo b a r', 'fo o b a r', 'f o o b a r']

提示:您可以考虑在每个位置拆分字符串,然后对每个子字符串进行递归相同的操作。 - Benjamin Hodgson
2
你怎么改变输出这么多了? - jamylak
8个回答

5
import itertools as it

def func(s):
   if not s:
       return [s]
   binary = it.product(['',' '], repeat=len(s)-1)
   zipped = (it.izip_longest(s , comb, fillvalue='') for comb in binary)
   return [''.join(it.chain.from_iterable(x)) for x in zipped]

func('foobar')

输出:

['foobar',
 'fooba r',
 'foob ar',
 'foob a r',
 'foo bar',
 'foo ba r',
 'foo b ar',
 'foo b a r',
 'fo obar',
 'fo oba r',
 'fo ob ar',
 'fo ob a r',
 'fo o bar',
 'fo o ba r',
 'fo o b ar',
 'fo o b a r',
 'f oobar',
 'f ooba r',
 'f oob ar',
 'f oob a r',
 'f oo bar',
 'f oo ba r',
 'f oo b ar',
 'f oo b a r',
 'f o obar',
 'f o oba r',
 'f o ob ar',
 'f o ob a r',
 'f o o bar',
 'f o o ba r',
 'f o o b ar',
 'f o o b a r']

@jamylak -- 不,它不会。 - root
你只有32种组合,而OP有64种。此外,你的组合是反过来的。像 for x in product(('', ' '), repeat=len(text)):L.append(''.join(chain.from_iterable(izip(text, reversed(x)))).rstrip()) 这样的代码应该可以解决这个问题。 - jamylak
@jamylak - 它确实提供了所有可能的组合 - 但不包括重复项。set(myres) == set(OPres) :) - root
1
你可以添加 if not s: return [s] 来支持空字符串。@jamylak:通过将 ['', ' '] 替换为 "01" 并在二进制(2)基数中从0计数到 2 **(len(s)-1)-1,很容易看出解决方案是正确的(注意:幂中的 -1 是因为要得到一根绳子的3个部分,我们只需要2个切口)。 - jfs
很棒的解决方案,但是在新版本中,izip_longest不再支持,请使用zip_longest代替。 - fudu

2

下面是我之前提出的递归思路的实现:

def string_spaces(s):
    ret = set([s])  # use a set rather than a list to prevent duplicates
    for i in range(1, len(s)):
        for fst in string_spaces(s[:i]):
            for snd in string_spaces(s[i:]):
                ret.add(fst + ' ' + snd)
    return ret

例子:

In [11]: string_spaces('foo')
Out[11]: set(['foo', 'f o o', 'f oo', 'fo o'])

注意:Python的递归限制为1000个堆栈帧,因此对于非常长的字符串(超过1000个字符),程序将崩溃。


2
from itertools import product

text = "foobar"
L = [''.join(reversed(x)).rstrip()
     for x in product(*[(c, c+' ') for c in reversed(text)])]
print L

['foobar', 'f oobar', 'fo obar', 'f o obar', 'foo bar', 'f oo bar', 'fo o bar', 'f o o bar', 'foob ar', 'f oob ar', 'fo ob ar', 'f o ob ar', 'foo b ar', 'f oo b ar', 'fo o b ar', 'f o o b ar', 'fooba r', 'f ooba r', 'fo oba r', 'f o oba r', 'foo ba r', 'f oo ba r', 'fo o ba r', 'f o o ba r', 'foob a r', 'f oob a r', 'fo ob a r', 'f o ob a r', 'foo b a r', 'f oo b a r', 'fo o b a r', 'f o o b a r', 'foobar', 'f oobar', 'fo obar', 'f o obar', 'foo bar', 'f oo bar', 'fo o bar', 'f o o bar', 'foob ar', 'f oob ar', 'fo ob ar', 'f o ob ar', 'foo b ar', 'f oo b ar', 'fo o b ar', 'f o o b ar', 'fooba r', 'f ooba r', 'fo oba r', 'f o oba r', 'foo ba r', 'f oo ba r', 'fo o ba r', 'f o o ba r', 'foob a r', 'f oob a r', 'fo ob a r', 'f o ob a r', 'foo b a r', 'f oo b a r', 'fo o b a r', 'f o o b a r']

非常优雅。我从未想过这种方式使用productreversed =) - alvas

1

这可能不是最有效的方法,但我会创建两个列表。一个列表中每个元素都是一个字母,另一个列表中每个元素都是字母后跟一个空格。(每次跳过最后一个字母,因为它总是没有空格。)通过为每个字母选择两个列表之一来生成可能的间距(可以将其建模为二进制数,其中0 = 无空格,1 = 空格)。

def spacify(word):
    no_space = list(word[:-1])
    spaced = [lt + ' ' for lt in no_space]
    for i in range(2 ** (len(word) - 1)):
        spaced_word = ""
        for j in range(len(word) - 1):
            if i % 2 == 0:
                spaced_word += no_space[j]
            else:
                spaced_word += spaced[j]
            i = i // 2 # Or use bit shifting to be fancy
    print spaced_word + word[-1]

不是通用解决方案,也不是高效的解决方案。 - Alagappan Ramu
实际上,你只需要进行2 ** 4(16种组合)即可,因为你必须去掉单词的第一个和最后一个字母。 - lucasg
1
解决方案并不完全正确,因为最后一个字母后面不应该有空格。这也减少了可能性。现在进行修复。 - Titandrake
1
实际上,这并不正确。这会生成添加空格的所有可能方式,但问题实际上并没有要求这样做。例如,“fo ob ar”没有列为示例。 - Titandrake
1
@Titandrake 我认为楼主也想要那些。但是在提供的示例中错过了它们。 - Alagappan Ramu

1
from itertools import combinations

def gen_spaces(data):
    return_value = []
    size = len(data)-1
    for num_spaces in range(size):
        for comb in combinations(range(size), num_spaces+1):
            data_as_list = list(data)
            for i in comb:
                data_as_list[i] +=' '
            return_value.append(''.join(data_as_list))
    return return_value

from pprint import pprint

pprint(gen_spaces("foobar"))

输出:

['f oobar',
 'fo obar',
 'foo bar',
 'foob ar',
 'fooba r',
 'f o obar',
 'f oo bar',
 'f oob ar',
 'f ooba r',
 'fo o bar',
 'fo ob ar',
 'fo oba r',
 'foo b ar',
 'foo ba r',
 'foob a r',
 'f o o bar',
 'f o ob ar',
 'f o oba r',
 'f oo b ar',
 'f oo ba r',
 'f oob a r',
 'fo o b ar',
 'fo o ba r',
 'fo ob a r',
 'foo b a r',
 'f o o b ar',
 'f o o ba r',
 'f o ob a r',
 'f oo b a r',
 'fo o b a r',
 'f o o b a r']

更新:

你提到需要"在字符之间加上空格的所有可能组合",但是你在[Out]中提供的示例并没有反映出这一点(即你有两个"f o o bar",缺少了"f ooba r"等)

在这个答案中,我假设你真的想要"在字符之间加上空格的所有可能组合"


1

递归解法。(对于较长的字符串可能需要使用sys.setrecursionlimit()):

def gen_perm(my_str):
    if len(my_str) <= 1 :
        return [my_str]
    rest_perms = gen_perm(my_str[1:])
    all_perms = [my_str[0] + perm  for perm in rest_perms ] + [my_str[0] + ' ' + perm for perm in rest_perms]
    return all_perms

print(gen_perm("foobar"))

0
这是一个关于回溯的问题。
此问题的伪解决方案如下:
Traverse(given_string, new_string + given_string[idx] ,idx+1)
Traverse(given_string, new_string + ' ' + given_string[idx] ,idx+1)

0
使用itertools库(但与Titandrake基本相同):
import itertools

foobar = "foobar"
foobar_r = range(len(foobar))


for integer in range(2**5):
    binary_mask = [ bit for bit in itertools.ifilter(lambda x: ( integer >>x)&0x01, foobar_r ) ] 
    spaces_mask = [ " " if i in binary_mask else ""  for i in foobar_r ]

    # Zip-it Crash-it Melt-it Upgrade-it
    print integer, "".join([ "".join([str(char) for char in zip_char ]) for zip_char in itertools.izip(foobar,spaces_mask)])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接