关于python:快速和pythonic的方式来找出一个字符串是一个回文

Fast and pythonic way to find out if a string is a palindrome

[编辑:有人指出我使用了不正确的palindrom概念,现在我已经编辑了正确的功能。我在第一个和第三个例子中也做了一些优化,其中for语句一直到它达到字符串的一半]

我已经为一个检查字符串是否为回文的方法编写了三个不同的版本。该方法实现为类"str"的扩展

该方法还将字符串转换为小写,并删除所有准时和空格。哪一个更好(更快,pythonic)?

以下是方法:

1)这是我想到的第一个解决方案:

1
2
3
4
5
6
7
    def palindrom(self):
        lowerself = re.sub("[ ,.;:?!]","", self.lower())
        n = len(lowerself)
        for i in range(n//2):
            if lowerself[i] != lowerself[n-(i+1)]:
               return False
        return True

我认为这个更快,因为没有字符串的转换或反转,并且for语句在第一个不同的元素处断开,但我不认为这是一种优雅和pythonic的方式

2)在第二个版本中,我使用在stackoverflow上创建的解决方案进行转换(使用高级切片字符串[:: - 1])

1
2
3
4
5
6
7
8
# more compact
def pythonicPalindrom(self):
    lowerself = re.sub("[ ,.;:?!]","", self.lower())
    lowerReversed = lowerself[::-1]
    if lowerself == lowerReversed:
        return True
    else:
        return False

但我认为切片和字符串之间的比较使这个解决方案变慢。

3)我想到的第三个解决方案,使用迭代器:

1
2
3
4
5
6
7
8
# with iterator
def iteratorPalindrom(self):
    lowerself = re.sub("[ ,.;:?!]","", self.lower())
    iteratorReverse = reversed(lowerself)
    for char in lowerself[0:len(lowerself)//2]:
        if next(iteratorReverse) != char:
            return False
    return True

我认为第一种解决方案更优雅,第二种解决方案效率更高


所以,我决定只是timeit,并找到哪一个是最快的。请注意,最终函数是您自己的pythonicPalindrome的更清晰版本。它的定义如下:

1
2
def palindrome(s, o):
    return re.sub("[ ,.;:?!]","", s.lower()) == re.sub("[ ,.;:?!]","", o.lower())[::-1]

方法

我为每个函数运行了10个不同的测试在每次测试运行中,函数被调用10000次,参数self="aabccccccbaa", other="aabccccccbaa"。结果可以在下面找到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
            palindrom       iteratorPalindrome      pythonicPalindrome      palindrome  
1           0.131656638            0.108762937             0.071676536      0.072031984
2           0.140950052            0.109713793             0.073781851      0.071860462
3           0.126966087            0.109586756             0.072349792      0.073776719
4           0.125113136            0.108729573             0.094633969      0.071474645
5           0.130878159            0.108602964             0.075770395      0.072455015
6           0.133569472            0.110276694             0.072811747      0.071764222
7           0.128642812            0.111065438             0.072170571      0.072285204
8           0.124896702            0.110218949             0.071898959      0.071841214
9           0.123841905            0.109278358             0.077430437      0.071747112
10          0.124083576            0.108184210             0.080211147      0.077391086

AVG         0.129059854            0.109441967             0.076273540      0.072662766
STDDEV      0.005387429            0.000901370             0.007030835      0.001781309

看起来pythonicPalindrome的清洁版本稍微快一点,但这两种功能都明显超出了替代品。


回想一下,filter适用于字符串:

1
2
3
>>> st="One string, with punc. That also needs lowercase!"
>>> filter(lambda c: c not in" ,.;:?!", st.lower())
'onestringwithpuncthatalsoneedslowercase'

所以你的测试可以是一个功能明显的单线程:

1
2
3
4
>>> str
'!esacrewol sdeen osla tahT .cnup htiw ,gnirts enO'
>>> filter(lambda c: c not in" ,.;:?!", st.lower())==filter(lambda c: c not in" ,.;:?!", str.lower()[::-1])
True

或者,如果您要使用正则表达式,只需使用惯用的str[::-1]反转结果:

1
2
3
4
>>>"123"[::-1]
'321'
>>> re.sub(r'[ ,.;:?!]', '', st.lower())==re.sub(r'[ ,.;:?!]', '', str.lower())[::-1]
True

最快的可能是使用string.tranlate删除字符:

1
2
3
4
5
>>> import string
>>> string.translate(st, None," ,.;:?!")
'OnestringwithpuncThatalsoneedslowercase'
>>> string.translate(st, None," ,.;:?!")==string.translate(str, None," ,.;:?!")[::-1]
True


你也可以计算这个不使用re的单行程,但是反之亦然:

1
2
def isPalindrom(self):
    return all(i==j for i, j in itertools.zip_longest((i.lower() for i in self if i not in" ,.;:?!"), (j.lower() for j in self[::-1] if j not in" ,.;:?!")))

或者,更详细地解释:

1
2
3
4
5
def isPalindrom(self):
    #using generators to not use memory
    stripped_self = (i.lower() for i in self if i not in" ,.;:?!")
    reversed_stripped_self = (j.lower() for j in self[::-1] if j not in" ,.;:?!")
    return all(self_char==reversed_char for self_char, reversed_char in itertools.zip_longest(stripped_self, reversed_stripped_self))


您似乎想知道代码块的执行时间并进行比较。

您可以使用timeit模块。

这是一个快速的方法:

1
2
3
4
5
6
7
8
9
import timeit

start = timeit.default_timer()

#Your code here

stop = timeit.default_timer()

print stop - start

Read more:

Option 1

Option 2


当我们传递一个单词时,它检查它是否可以反转,如果它可以被反转,则打印"这是一个回文"。或者"这不是回文"

1
2
3
4
5
6
7
8
9
10
11
12
13
def reverse(word):
    x = ''
    for i in range(len(word)):
        x += word[len(word)-1-i]
        return x

word = input('give me a word:
'
)
x = reverse(word)
if x == word:
    print('This is a Palindrome')
else:
    print('This is NOT a Palindrome')