【问题标题】:Removing duplicates if paired adjacently如果相邻配对,则删除重复项
【发布时间】:2016-08-06 09:16:02
【问题描述】:

删除任何一对具有相同值的相邻字母。例如,字符串“aabcc”在操作后会变成“aab”或“bcc”。

样本输入 = aaabccddd
样本输出 = abd

对如何以匹配重复项并删除它们的方式迭代列表或字符串感到困惑,这是我正在尝试的方式,我知道这是错误的。

S = input()
removals = []

for i in range(0, len(S)):
    if i + 1 >= len(S):
        break

    elif S[i] == S[i + 1]:
        removals.append(i)    
        # removals is to store all the indexes that are to be deleted.
        removals.append(i + 1)
        i += 1
    print(i)
Array = list(S)
set(removals)    #removes duplicates from removals

for j in range(0, len(removals)):
    Array.pop(removals[j])    # Creates IndexOutOfRange error

这是来自 Hackerrank 的问题:Super Reduced String

【问题讨论】:

  • 为什么是aab?那就是 aa 是输入样本中的相邻对。
  • 你没有很好地解释 Hackerrank 问题。那里的样本是aabcc(2 次 c,而不是 3 次),他们正在讨论系列中的一个操作
  • aab 因为它正在删除作为一项操作的 cc,而在另一项操作中它将删除 aa 并形成 b。还更正了那个,仅提供链接是因为我无法正确解释问题。

标签: python arrays string list


【解决方案1】:

如果有偶数个字母,则删除成对的字母可以减少为一个空序列,如果有奇数个则为 1。 aaaaaa 变为空,aaaaa 减少为 a

要对任何序列执行此操作,请使用 itertools.groupby() 并计算组大小:

# only include a value if their consecutive count is odd
[v for v, group in groupby(sequence) if sum(1 for _ in group) % 2]

然后重复直到序列的大小不再改变:

prev = len(sequence) + 1
while len(sequence) < prev:
    prev = len(sequence)
    sequence = [v for v, group in groupby(sequence) if sum(1 for _ in group) % 2]

但是,由于 Hackerrank 为您提供文本,如果您使用正则表达式执行此操作会更快:

import re

even = re.compile(r'(?:([a-z])\1)+')

prev = len(text) + 1
while len(text) < prev:
    prev = len(text)
    text = even.sub(r'', text)

[a-z] 在正则表达式中匹配小写字母,(..)groups that match, and\1references the first match and will only match if that letter was repeated.(?:...)+asks for repeats of the same two characters.re.sub()` 将所有这些模式替换为空文本。

正则表达式方法足以通过 Hackerrank 挑战。

【讨论】:

  • 感谢 正则表达式使它变得简单快捷,因为它不需要迭代。
  • @YashAgarwal:实际上,基于堆栈的方法要快得多,因为它的时间复杂度为 O(n)。当在 Hackerrank 挑战中 N=100 时,这无关紧要,但如果有一个字符串,比如说 1000000 个字符,你会看到最坏情况下的性能有很大差异。
  • @niemmi: 这就是 Hackerrank 给出这些数字的原因 :-) 在这么低的N 上,重复几次正则表达式(没有任何回溯问题)将击败一个堆栈每步循环固定时间。
  • @MartijnPieters:即使 N=100,它也完全取决于输入。在某些情况下,正则表达式比堆栈快大约 5 倍,但在最坏的情况下,正则表达式大约需要基于堆栈的方法的 10 倍。
  • @niemmi:感谢您添加基准,这很有趣!
【解决方案2】:

您可以使用堆栈来实现 O(n) 时间复杂度。遍历字符串中的字符,并为每个字符检查堆栈顶部是否包含相同的字符。如果它确实从堆栈中弹出字符并移动到下一个项目。否则将角色推入堆栈。堆栈中剩下的就是结果:

s = 'aaabccddd'
stack = []

for c in s:
    if stack and stack[-1] == c:
        stack.pop()
    else:
        stack.append(c)

print ''.join(stack) if stack else 'Empty String' # abd

更新根据讨论,我运行了几个测试来测量输入长度为100 的正则表达式和基于堆栈的解决方案的速度。测试在 Windows 8 上的 Python 2.7 上运行:

All same
Regex: 0.0563033799756
Stack: 0.267807865445
Nothing to remove
Regex: 0.075074750044
Stack: 0.183467329017
Worst case
Regex: 1.9983200193
Stack: 0.196362265609
Alphabet
Regex: 0.0759905517997
Stack: 0.182778728207

用于基准测试的代码:

import re
import timeit

def reduce_regexp(text):
    even = re.compile(r'(?:([a-z])\1)+')

    prev = len(text) + 1
    while len(text) < prev:
        prev = len(text)
        text = even.sub(r'', text)

    return text

def reduce_stack(s):
    stack = []

    for c in s:
        if stack and stack[-1] == c:
            stack.pop()
        else:
            stack.append(c)

    return ''.join(stack)


CASES = [
    ['All same', 'a' * 100],
    ['Nothing to remove', 'ab' * 50],
    ['Worst case', 'ab' * 25 + 'ba' * 25],
    ['Alphabet', ''.join([chr(ord('a') + i) for i in range(25)] * 4)]
]

for name, case in CASES:
    print(name)
    res = timeit.timeit('reduce_regexp(case)',
                        setup='from __main__ import reduce_regexp, case; import re',
                        number=10000)
    print('Regex: {}'.format(res))
    res = timeit.timeit('reduce_stack(case)',
                        setup='from __main__ import reduce_stack, case',
                        number=10000)
    print('Stack: {}'.format(res))

【讨论】:

  • 代码运行良好,但我无法理解 if 部分,请给我一些时间弄清楚。谢谢你的代码看起来很优雅。
  • @YashAgarwal:if 的第一部分检查 stack 是否包含任何项目,因为在 Python 中空序列在布尔上下文中被视为 False。仅在 stack 非空的情况下才被评估的第二部分返回来自 stack 的最后一项并将其与当前字符进行比较。
  • 知道了,做得这么好,谢谢,明白这样的问题我们必须使用列表和堆栈操作。
  • 不要使用time.clock();使用timeit 模块进行适当的计时试验(因为它会为您的平台选择正确的时钟、禁用 GC 等)。 from timeit import timeitduration = timeit('f(case)', 'from __main__ import case, reduce_regexp as f', number=10000)
  • @MartijnPieters:已更新,不会以某种方式改变结果。唯一的变化是最坏情况下的数字,因为我固定了输入长度,但比率保持不变。
猜你喜欢
  • 1970-01-01
  • 2015-11-29
  • 2018-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-05
  • 1970-01-01
相关资源
最近更新 更多