【问题标题】:python string split by separator all possible permutationspython字符串按分隔符拆分所有可能的排列
【发布时间】:2021-12-01 22:31:11
【问题描述】:

这可能与 Python 3.3: Split string and create all combinations 类似的问题密切相关,但我无法从中推断出 Pythonic 解决方案。

问题是:

假设有一个str,例如'hi|guys|whats|app',我需要用分隔符分割那个str的所有排列。示例:

#splitting only once
['hi','guys|whats|app']
['hi|guys','whats|app']
['hi|guys|whats','app']
#splitting only twice
['hi','guys','whats|app']
['hi','guys|whats','app']
#splitting only three times
...
etc

我可以编写回溯算法,但是 python(例如 itertools)是否提供了简化该算法的库?

提前致谢!!

【问题讨论】:

  • 您可以使用.split("|", n) 来完成,其中 n 是您需要的最大拆分数
  • 那不会做他想做的事。
  • @MohamedYahya 是的,但它也会像['hi','guys','whats|app']一样返回
  • 是的,我知道他需要什么,谢谢????
  • 您有n 可能的位置,您可以选择拆分或不拆分。找到所有可能的分割位置的索引。选择其中的k,并在这些位置执行拆分。对于给定k 拆分的所有可能字符串,您只是在寻找从n 中选择k 元素的所有可能方法。不过,它的增长速度非常快,因此很快就会变得非常棘手。

标签: python string split permutation


【解决方案1】:

如果你想要所有个分区,试试partitions from more-itertools:

from more_itertools import partitions

s = 'hi|guys|whats|app'

for p in partitions(s.split('|')):
    print(list(map('|'.join, p)))

输出:

['hi|guys|whats|app']
['hi', 'guys|whats|app']
['hi|guys', 'whats|app']
['hi|guys|whats', 'app']
['hi', 'guys', 'whats|app']
['hi', 'guys|whats', 'app']
['hi|guys', 'whats', 'app']
['hi', 'guys', 'whats', 'app']

如果您只想要一定数量的拆分,那么不要在 all 分隔符处拆分然后重新连接部分,您可以获取分隔符索引的组合并相应地获取子字符串:

from itertools import combinations

s = 'hi|guys|whats|app'
splits = 2

indexes = [i for i, c in enumerate(s) if c == '|']
for I in combinations(indexes, splits):
    print([s[i+1:j] for i, j in zip([-1, *I], [*I, None])])

输出:

['hi', 'guys', 'whats|app']
['hi', 'guys|whats', 'app']
['hi|guys', 'whats', 'app']

【讨论】:

    【解决方案2】:

    一种方法,分割字符串后,使用itertools.combinations定义列表中的分割点,其他位置应再次融合。

    def lst_merge(lst, positions, sep='|'):
        '''merges a list on points other than positions'''
        '''A, B, C, D and 0, 1 -> A, B, C|D'''
        a = -1
        out = []
        for b in list(positions)+[len(lst)-1]:
            out.append('|'.join(lst[a+1:b+1]))
            a = b
        return out
    
    def split_comb(s, split=1, sep='|'):
        from itertools import combinations
        l = s.split(sep)
        return [lst_merge(l, pos, sep=sep)
                for pos in combinations(range(len(l)-1), split)]
    

    例子

    >>> split_comb('hi|guys|whats|app', 0)
    [['hi|guys|whats|app']]
    
    >>> split_comb('hi|guys|whats|app', 1)
    [['hi', 'guys|whats|app'],
     ['hi|guys', 'whats|app'],
     ['hi|guys|whats', 'app']]
    
    >>> split_comb('hi|guys|whats|app', 2)
    [['hi', 'guys', 'whats|app'],
     ['hi', 'guys|whats', 'app'],
     ['hi|guys', 'whats', 'app']]
    
    >>> split_comb('hi|guys|whats|app', 3)
    [['hi', 'guys', 'whats', 'app']]
    
    >>> split_comb('hi|guys|whats|app', 4)
    [] ## impossible
    

    理由

    ABCD -> A B C D
             0 1 2
    
    combinations of split points: 0/1 or 0/2 or 1/2
    
    0/1 -> merge on 2 -> A B CD
    0/2 -> merge on 1 -> A BC D
    1/2 -> merge on 0 -> AB C D
    

    通用函数

    这是一个通用版本,与上面一样工作,但也将-1 作为split 的参数,在这种情况下它将输出所有组合

    def lst_merge(lst, positions, sep='|'):
        a = -1
        out = []
        for b in list(positions)+[len(lst)-1]:
            out.append('|'.join(lst[a+1:b+1]))
            a = b
        return out
    
    def split_comb(s, split=1, sep='|'):
        from itertools import combinations, chain
        
        l = s.split(sep)
        
        if split == -1:
            pos = chain.from_iterable(combinations(range(len(l)-1), r)
                                      for r in range(len(l)+1))
        else:
            pos = combinations(range(len(l)-1), split)
            
        return [lst_merge(l, pos, sep=sep)
                for pos in pos]
    

    示例:

    >>> split_comb('hi|guys|whats|app', -1)
    [['hi|guys|whats|app'],
     ['hi', 'guys|whats|app'],
     ['hi|guys', 'whats|app'],
     ['hi|guys|whats', 'app'],
     ['hi', 'guys', 'whats|app'],
     ['hi', 'guys|whats', 'app'],
     ['hi|guys', 'whats', 'app'],
     ['hi', 'guys', 'whats', 'app']]
    

    【讨论】:

    • 很多好的答案,但我喜欢将拆分参数化为能够仅生成所需数量的令牌。这正是我想要的,谢谢@mozway!
    • 不客气@glezo 出于好奇,这段代码的用例是什么?
    • 难点:csv 由管道(“|”)分隔,某些字段管道字符无法插入 pandas 数据帧。因此,我将尝试将这些错误行标记为所有可能的排列,并将它们插入到 pandas 的数据框 =D
    • :D 我没有看到那个来。希望它有效。
    【解决方案3】:

    您可以找到index all '|' 然后在所有组合中将'|' 替换为',' 然后拆分基',',如下所示:

    >>> from itertools import combinations
    >>> st = 'hi|guys|whats|app'
    >>> idxs_rep = [idx for idx, s in enumerate(st) if s=='|']
    
    >>> def combs(x):
    ...    return [c for i in range(len(x)+1) for c in combinations(x,i)]
    
    >>> for idxs in combs(idxs_rep):        
    ...    lst_st = list(st)
    ...    for idx in idxs:
    ...        lst_st[idx] = ','
    ...    st2 = ''.join(lst_st)
    ...    print(st2.split(','))
    
    ['hi|guys|whats|app']
    ['hi', 'guys|whats|app']
    ['hi|guys', 'whats|app']
    ['hi|guys|whats', 'app']
    ['hi', 'guys', 'whats|app']
    ['hi', 'guys|whats', 'app']
    ['hi|guys', 'whats', 'app']
    ['hi', 'guys', 'whats', 'app']
    

    【讨论】:

      【解决方案4】:

      使用combinationschain 的一种方法

      from itertools import combinations, chain
      
      
      def partition(alist, indices):
          # https://stackoverflow.com/a/1198876/4001592
          pairs = zip(chain([0], indices), chain(indices, [None]))
          return (alist[i:j] for i, j in pairs)
      
      
      s = 'hi|guys|whats|app'
      delimiter_count = s.count("|")
      splits = s.split("|")
      
      
      for i in range(1, delimiter_count + 1):
          print("split", i)
          for combination in combinations(range(1, delimiter_count + 1), i):
              res = ["|".join(part) for part in partition(splits, combination)]
              print(res)
      

      输出

      split 1
      ['hi', 'guys|whats|app']
      ['hi|guys', 'whats|app']
      ['hi|guys|whats', 'app']
      split 2
      ['hi', 'guys', 'whats|app']
      ['hi', 'guys|whats', 'app']
      ['hi|guys', 'whats', 'app']
      split 3
      ['hi', 'guys', 'whats', 'app']
      

      这个想法是生成所有方法来选择(或删除)分隔符 1、2、3 次并从那里生成分区。

      【讨论】:

        【解决方案5】:

        这是我想出的递归函数:

        def splitperms(string, i=0):
            if len(string) == i:
                return [[string]]
            elif string[i] == "|":
                return [*[[string[:i]] + split for split in splitperms(string[i + 1:])], *splitperms(string, i + 1)]
            else:
                return splitperms(string, i + 1)
        

        输出:

        >>> splitperms('hi|guys|whats|app')
        [['hi', 'guys', 'whats', 'app'], ['hi', 'guys', 'whats|app'], ['hi', 'guys|whats', 'app'], ['hi', 'guys|whats|app'], ['hi|guys', 'whats', 'app'], ['hi|guys', 'whats|app'], ['hi|guys|whats', 'app'], ['hi|guys|whats|app']]
        >>> 
        

        【讨论】:

        • 这不是预期的输出 ;)
        • @mozway 为什么? :(
        • 你的函数不应该只给出给定数量的分割吗?这会产生所有
        • 我想没关系
        • @mozway 让我们看看 OP 是怎么想的
        猜你喜欢
        • 2011-06-09
        • 1970-01-01
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多