Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

[Edit: as someone pointed out I have used improperly the palindrom concept, now I have edited with the correct functions. I have done also some optimizations in the first and third example, in which the for statement goes until it reach half of the string]

I have coded three different versions for a method which checks if a string is a palindrome. The method are implemented as extensions for the class "str"

The methods also convert the string to lowercase, and delete all the punctual and spaces. Which one is the better (faster, pythonic)?

Here are the methods:

1) This one is the first solution that I thought of:

    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

I think that this one is the more faster because there aren't transformations or reversing of the string, and the for statement breaks at the first different element, but I don't think it's an elegant and pythonic way to do so

2) In the second version I do a transformation with the solution founded here on stackoverflow (using advanced slicing string[::-1])

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

But I think that the slicing and the comparision between the strings make this solution slower.

3) The thirds solution that I thought of, use an iterator:

# 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

which I think is way more elegant of the first solution, and more efficient of the second solution

like image 201
Nikaido Avatar asked Jan 06 '16 15:01

Nikaido


People also ask

How do you check if a string is palindrome or not?

A string is said to be palindrome if it reads the same backward as forward. For e.g. above string is a palindrome because if we try to read it from backward, it is same as forward. One of the approach to check this is iterate through the string till middle of string and compare a character from back and forth.

How do you check if a string can be rearranged to form a palindrome?

A set of characters can form a palindrome if at most one character occurs an odd number of times and all characters occur an even number of times. A simple solution is to run two loops, the outer loop picks all characters one by one, and the inner loop counts the number of occurrences of the picked character.

How do you check if a string has a palindrome in it python?

Explanation: In the above program, first take input from the user (using input OR raw_input() method) to check for palindrome. Then using slice operation [start:end:step], check whether the string is reversed or not. Here, step value of -1 reverses a string. If yes, it prints a palindrome else, not a palindrome.


2 Answers

So, I decided to just timeit, and find which one was the fastest. Note that the final function is a cleaner version of your own pythonicPalindrome. It is defined as follows:

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

Methodology

I ran 10 distinct tests per function. In each test run, the function was called 10000 times, with arguments self="aabccccccbaa", other="aabccccccbaa". The results can be found below.

            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

It would appear that the cleaner version of your pythonicPalindrome is marginally faster, but both functions clearly outclass the alternatives.

like image 70
Niels Wouda Avatar answered Oct 03 '22 06:10

Niels Wouda


It seems that you want to know the execution time of your blocks of code and compare them.

You can use the timeit module.

Here's a quick way:

import timeit

start = timeit.default_timer()

#Your code here

stop = timeit.default_timer()

print stop - start 

Read more:

Option 1

Option 2

like image 28
dot.Py Avatar answered Oct 03 '22 07:10

dot.Py