【问题标题】:Switch every pair of characters in a string切换字符串中的每一对字符
【发布时间】:2015-08-18 03:21:45
【问题描述】:

例如,有字符串:

abcdefghijklmnopqrstuvwxyz

应该是这样的:

badcfehgjilknmporqtsvuxwzy

我该怎么做呢?

我想到了一些效率不高的东西,比如:

s = str(range(ord('a'), ord('z') + 1))
new_s = ''
for i in xrange(len(s)):
    if i != 0 and i % 2 == 0:
        new_s += '_' + s[i]
    else:
        new_s += s[i]
# Now it should result in a string such as 'ab_cd_ef_...wx_yz'
l = new_s.split('_')
for i in xrange(len(l)):
    l[i] = l[i][::-1]
result = str(l)

有没有更好的方法?某种更有效或更通用的方法,所以我也可以更轻松地用 3 个字母来处理它?

【问题讨论】:

  • 长度会一直均匀吗?
  • @thefourtheye 没有**。顺便说一句,我发布的方式仍然适用于长度不均匀的字符串。
  • 如果你想得到 3 个字符,你期望结果是什么?
  • it = iter(s);''.join(next(it, '') + c for c in it )干净简单的IMO。
  • @AshwiniChaudhary 请发布答案,以便我将其标记为最适合未来读者,您的答案是最快的。

标签: python string performance python-2.7


【解决方案1】:

您可以使用zip() 函数将元组列表返回为[(b,a), (d,c), ...],并将.join() 方法应用于元组和列表的元素。

a = "abcdefghijklmnopqrstuvwxyz"
# a[::2] = "acegikmoqsuwy"
# a[1::2] = "bdfhjlnprtvx"
print "".join("".join(i) for i in zip(a[1::2], a[::2]))
>>> badcfehgjilknmporqtsvuxwzy

编辑:如@Ashwini 和@TigerhawkT3 所建议,要处理奇数长度字符串的情况,您可以将代码更改为:

print "".join("".join(i) for i in zip(a2, a1)) + a[-1] if len(a)%2 else '' 

【讨论】:

  • 奇数字符串呢?
  • 更新了我的答案,谢谢@AshwiniChaudhary。
  • 感谢@TigerhawkT3 即兴创作我的回答内容。
【解决方案2】:

不使用任何导入的一种解决方案是将字符串转换为迭代器,并在迭代期间通过在迭代器上调用 next 来获取下一个字符:

>>> s = "abcdefghijklmnopqrstuvwxyz"
>>> it = iter(s)
>>> ''.join(next(it, '') + c for c in it )
'badcfehgjilknmporqtsvuxwzy'

时间安排:

>>> s = "abcdefghijklmnopqrstuvwxyz" * 10**5
>>> def func_next_no_cache(s):
    it = iter(s)
    return ''.join([next(it, '') + c for c in it])
...
>>> %timeit func_next_no_cache(s)
1 loops, best of 3: 291 ms per loop

但是对next 的调用实际上会减慢它的速度,因为要找到next,Python 必须从本地范围开始进入内置函数,让我们缓存它并再试一次:

>>> def func_next_cache(s, next=next):
    it = iter(s)
    return ''.join([next(it, '') + c for c in it])
...
>>> %timeit func_next_cache(s)
1 loops, best of 3: 241 ms per loop

但最快的解决方案是使用itertools.izip_longest

>>> from itertools import izip_longest
>>> def func_izip_l(s):
    it = iter(s)
    return "".join([b+a for a, b in  izip_longest(it, it, fillvalue='')])
...
>>> %timeit func_izip_l(s)

1 loops, best of 3: 209 ms per loop

@Joran 的代码在与列表而不是生成器表达式一起使用时也非常接近这个,但它在内存中创建了两个额外的字符串:

>>> %timeit "".join([b+a for a, b in izip_longest(s[::2], s[1::2], fillvalue="")])
1 loops, best of 3: 212 ms per loop

注意如果速度是一个问题,我们应该始终将list 发送给str.joinhttps://stackoverflow.com/a/9061024/846892

【讨论】:

  • 最快的解决方案。谢谢你:)
【解决方案3】:

我不确定首先使用正则表达式总是最好的做法,但它似乎适合这里。找到 2 个字符,以相反的顺序将它们替换,然后继续直到你用完字符串。

import re

>>> s = "abcdefghijklmnopqrstuvwxyz"
>>> re.sub(r'(.)(.)', "\g<2>\g<1>", s)
'badcfehgjilknmporqtsvuxwzy'

很容易推广到其他数量的字符:

>>> def swap3(txt):
...    return re.sub(r'(.)(.)(.)', '\g<3>\g<2>\g<1>', txt)
...
>>> swap3(s)
'cbafedihglkjonmrqputsxwvyz'

>>> def parameterizedSwap(txt, numChars):
...    pat = r"(.)" * numChars
...    replace = "".join(["\g<{0}>".format(numChars-i) for i in range(numChars)])
...    return re.sub(pat, replace, txt)
...
>>> parameterizedSwap(s, 5)
'edcbajihgfonmlktsrqpyxwvuz'

【讨论】:

  • 同意,虽然分块和 zip 是我首先想到的方法,但正则表达式更易于阅读且更具可扩展性。
  • 是的,这就是为什么我赞成这个......很好,很短,很容易阅读(也许让第二个参数可选(.?)来处理奇数长度的字符串?)
【解决方案4】:
from itertools import izip_longest as myzip
"".join(b+a for a,b in myzip(a[::2],a[1::2],fillvalue=""))

这与其他答案非常相似,只是更明确地向代码读者解释了它正在做什么

【讨论】:

    【解决方案5】:
    from itertools import zip, chain
    
    c1 = [c for i, c in enumerate(s) if i % 2 == 0]
    c2 = [c for i, c in enumerate(s) if i % 2 == 1]
    ''.join(chain.from_iterable(zip(c2,c1)))
    

    【讨论】:

      【解决方案6】:

      遍历字符对并将它们与izip() 连接起来相当简单,并且可以通过在末尾添加条件连接来处理奇数字符串长度的处理。

      from itertools import izip
      
      s = "abcdefghijklmnopqrstuvwxyz"
      print ("".join(((pair[1]+pair[0]) for pair in izip(*[iter(s)]*2))) +
                  (s[-1] if len(s) % 2 else ''))
      

      正如@Ashwini 在评论中所建议的那样,使用izip_longest() 而不是izip() 可以更简洁地完成同样的事情。

      from itertools import izip_longest
      
      s = "abcdefghijklmnopqrstuvwxyz"
      print "".join(((pair[1]+pair[0]) for pair in
                          izip_longest(*[iter(s)]*2, fillvalue='')))
      

      【讨论】:

      • 这对于奇数长度的字符串也会失败。 izip_longestfillvalue='' 将是这里的通用解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-29
      • 2012-03-15
      • 2021-03-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多