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 |
我认为第一种解决方案更优雅,第二种解决方案效率更高
所以,我决定只是
1 2 | def palindrome(s, o): return re.sub("[ ,.;:?!]","", s.lower()) == re.sub("[ ,.;:?!]","", o.lower())[::-1] |
方法
我为每个函数运行了10个不同的测试在每次测试运行中,函数被调用10000次,参数
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 |
看起来
回想一下,
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 |
或者,如果您要使用正则表达式,只需使用惯用的
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') |