【问题标题】:Split string by unique patterns按唯一模式拆分字符串
【发布时间】:2016-05-16 22:52:52
【问题描述】:

我似乎无法在以下问题上找到有效的答案:

假设我返回了以下 LONG int(或任何字符串):

longnr = 26731623516244147357200698985770699644500911065919594945175038371437093557337774208653

我可以通过以下方式轻松拆分:

splitnr = [26731,62351,624,41,4735,720,0698,9857,7069,964,450,091,10659,195,94,94517,5038,3714,3709,35,573,37,7,74208653]

每个组合都是唯一的,因此没有重复的数字。 我在简单的代码中通过迭代每个项目并将它们添加到一个字符串,直到它找到一个重复的数字来做到这一点。然后把这个字符串写在一个列表中,空字符串,添加刚刚检查的数字并继续直到全部完成。

我想要的是尽可能少的组合。 因此,首先尝试找出所有 10 位的唯一组合,然后再找到 9 个,其余的为 ,8,7 等

我需要正则表达式吗? 我无法完成这项工作,有人建议我需要大量模式。

下一个选项:

len(set(str(longnr)[0:10])) == len(str(longnr)[0:10])

这适用于前 10 个检查它是否是唯一的。

如何以最佳方式从这里出发?
必须像 splitnr 一样保持顺序。

【问题讨论】:

  • 这里绝对不是使用正则表达式的地方。
  • 我是这么想的,例如:*.com/questions/12870489/…我做不到。
  • 这只是为了好玩还是你想解决一些实际问题?如果你的字符串只是0000000.....000,会发生什么?每个子字符串的最大长度是 10 吗?这必须是完美的吗?
  • 有两个长度为 9 的子字符串还是 10 之一和 8 之一更好?
  • 既有趣又有问题 :) 如果我的字符串是 '000000',结果将是 [0,0,0,0,0,0] 如果我的字符串是 'abcfeagd',结果必须是 [abcfe,agd] 越大,事情就会发生变化: 'abcfeagdqp' : 结果必须是 [a,bcfeagdqp] 现在可以形成更大的独特组合。 2*length 9 或 8 + 10 没关系。

标签: python string algorithm split pattern-matching


【解决方案1】:

我确信 Edward Peters 有答案。但从经验上看,这三种解决方案似乎都一样好:

from random import choice

def edward_peters(string):
    sequences = [[]]
    for end in range(1, len(string) + 1):
        def candidate_sequences():
            for previous_end in range(max(0, end - 10), end):
                substring = string[previous_end:end]
                if len(substring) == len(set(substring)):
                    yield sequences[previous_end] + [substring]

        sequences.append(min(candidate_sequences(), key=len))
    return sequences[-1]

def brendan_abel(long_string):
    if not long_string:
        return []
    cur_i = None
    cur_s = None
    max_i = None
    max_s = None
    for i, s in enumerate(long_string):
        if cur_s is None or s in cur_s:
            if cur_s and (max_s is None or len(cur_s) > len(max_s)):
                max_i = cur_i
                max_s = cur_s
            cur_i = i
            cur_s = [s]
        else:
            cur_s.append(s)
    else:
        if cur_s and (max_s is None or len(cur_s) > len(max_s)):
            max_i = cur_i
            max_s = cur_s
    before = long_string[:max_i]
    after = long_string[max_i + len(max_s):]
    return brendan_abel(before) + [''.join(max_s)] + brendan_abel(after)

def ruud(string):
    result = []
    current = ''
    for c in string:
        if c in current:
            result.append(current)
            current = c
        else:
            current += c
    result.append(current)
    return result

def main():
    while True:
        string = ''.join(choice('1234567890') for _ in range(10000))
        results = [func(string) for func in [edward_peters, brendan_abel, ruud]]
        assert all(''.join(result) == string for result in results)
        assert len(set(map(len, results))) == 1

main()

我根本无法直观地掌握这一点。似乎 Brendan Abel 是对的,Edward Peters 的解决方案是 OP 的逆向工作,例如

print edward_peters(string)
['49', '9', '3849', '3089', '91', '1', '15', '58', '42876', '81926', '6720', '90', '0', '27103', '3064', '436', '6', '862', '2', '201', '7091', '912', '23', '6345', '582', '382', '2', '82457', '64937', '0574', '2743', '983', '4382']

【讨论】:

  • 感谢您的工作。事实上,所有相同的最终结果,仍然不是最优的。我会继续努力,找到了另一种方法。
  • 我真的怀疑有没有更好的方法。如果您有其他算法,请分享,我很想看看。
  • 嗯,我试过了,只是接近上面,没有改进。猜猜这只是最理想的方式。
【解决方案2】:

您可以使用递归函数来执行此操作,它的工作方式类似于分治排序算法。最坏的情况是O(n^2)(嗯,基本上总是O(n^2)

  1. 遍历原始列表中的每个元素,并尝试制作可能最长的唯一链,存储最大链的起始索引(我们可以在这里作弊,因为我们知道 10 是仅使用数字和找到 10 长链后立即停止,但在下面的示例中我没有这样做)。

  2. 将列表拆分为 2 个单独的列表,一个在我们刚刚找到的元素列表之前,一个在之后,并将它们都输入到递归函数中。

  3. 重新组合结果

下面的函数假设你传递给它一个字符串,所以首先将你的长数字字符串化,然后再将字符串转换回数字。

def func(long_string):
    if not long_string:
        return []
    cur_i = None
    cur_s = None
    max_i = None
    max_s = None
    for i, s in enumerate(long_string):
        if cur_s is None or s in cur_s:
            if cur_s and (max_s is None or len(cur_s) > len(max_s)):
                max_i = cur_i
                max_s = cur_s
            cur_i = i
            cur_s = [s]
        else:
            cur_s.append(s)
    else:
        if cur_s and (max_s is None or len(cur_s) > len(max_s)):
            max_i = cur_i
            max_s = cur_s
    before = long_string[:max_i]
    after = long_string[max_i + len(max_s):]
    return func(before) + [''.join(max_s)] + func(after)

long_number = 1233423732174096361032409234987352
list_of_strings = func(str(long_number).strip('L'))  # strip L for older python versions.
list_of_numbers = map(int, list_of_strings)

【讨论】:

  • 我对这个算法寄予厚望(即将编写相同的代码),但它似乎返回与 OP 相同数量的组合。
  • 我很确定问题中提出的简单算法总是给出最佳答案。它在 O(n) 时间内运行。
  • 除此之外将选择最长可能的唯一元素链(即[a,bcfeagdqp]而不是[abcfe, agdqp]
  • 啊,我明白你在做什么了。但 OP 似乎只关心最小化链的数量。
  • @BrendanAbel 我添加了一个比较三种解决方案的答案。另请注意,for 之后的 else 块始终会执行,因为没有 break