切换字符串中的每对字符

时间:2015-06-03 18:44:33

标签: python string performance python-2.7

例如,拥有字符串:

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个字母来处理它?<​​/ p>

6 个答案:

答案 0 :(得分:7)

您可以使用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 '' 

答案 1 :(得分:3)

不使用任何导入的一个解决方案是将字符串转换为迭代器,并在迭代期间通过在迭代器上调用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

答案 2 :(得分:2)

我不确定首先达到正则表达式总是最好的做法,但它似乎适合这里。找到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'

答案 3 :(得分:1)

from itertools import izip_longest as myzip
"".join(b+a for a,b in myzip(a[::2],a[1::2],fillvalue=""))

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

答案 4 :(得分:0)

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)))

答案 5 :(得分:0)

迭代一对字符并用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 ''))

使用izip_longest()代替izip()可以更简洁地完成同样的事情,正如@Ashwini在评论中所暗示的那样。

from itertools import izip_longest

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