Skip to content

Instantly share code, notes, and snippets.

@tomviner
Created September 16, 2011 08:47
Show Gist options
  • Save tomviner/1221595 to your computer and use it in GitHub Desktop.
Save tomviner/1221595 to your computer and use it in GitHub Desktop.
Anagram of Palindrome
# -*- coding: utf-8 -*-
"""
1) A string is a palindrome if it reads the same from left-to-right as it does right-to-left.
e.g “nolemonnomelon”, “racecar” & “neveroddoreven” are all palindromes.
A string is an anagram of another string if it consists of exactly the same characters but in another order.
e.g The string “carrace” is an anagram of “racecar”.
Write a function `def is_anagram_of_palindrome(str):`
such that it returns True if the string str is an anagram of a palindrome, and otherwise returns False.
You may assume that str contains only lowercase characters a-z and is not empty.
e.g Given str = “carrace” the function should return True as it is an anagram of the palindrome “racecar”.
Given str = “tangent” the function should return False as while it is an anagram,
it is not possible that it is an anagram of a palindrome.
We are not dealing with English words here, “bbcbb” is still a palindrome.
"""
def is_anagram_of_palindrome(str):
"""
Note: 'aa' is considered to have no anagrams, as 'aa' (other way round) is the same string.
Also: It's generally best not to use the python string constructor as a variable name :-)
>>> is_anagram_of_palindrome('')
False
>>> any(is_anagram_of_palindrome(n*'a') for n in range(1, 10)) # all already ordered as only palindrome
False
>>> any(is_anagram_of_palindrome(n*'a' + 'b' + n*'a') for n in range(1, 10)) # all already ordered as only palindrome
False
>>> is_anagram_of_palindrome('ab')
False
>>> is_anagram_of_palindrome('aab')
True
>>> is_anagram_of_palindrome('aba') # already ordered as only palindrome
False
>>> is_anagram_of_palindrome('abab')
True
>>> is_anagram_of_palindrome('abba') # has one other palindrome
True
>>> is_anagram_of_palindrome('abcba') # has one other palindrome
True
>>> is_anagram_of_palindrome('aabc')
False
>>> is_anagram_of_palindrome('aaabb')
True
>>> is_anagram_of_palindrome('aaabc')
False
>>> is_anagram_of_palindrome('aaaabb')
True
>>> is_anagram_of_palindrome('aaaabbc')
True
>>> is_anagram_of_palindrome('tangent')
False
>>> is_anagram_of_palindrome('nolemonnomelon') # already palindrome but others possible
True
"""
letter_counts = [str.count(ch) for ch in set(str)]
num_odd_letters = len([n for n in letter_counts if n%2])
num_even_letters = len(letter_counts) - num_odd_letters
# characters in a palindromes can pair off from the ends, leaving 0 or 1
# characters in the middle. Any middle character always appears an odd number
# of times in the string
can_form_palindrome = num_odd_letters <= 1
# a palindrome can only form another palindrome if it contains more than
# one type of letter to swap positions (eg abba --> baab), excluding a possible
# centre letter in odd length strings
is_only_palindrome = (num_even_letters <= 1) if (str==str[::-1]) else False
return can_form_palindrome and not is_only_palindrome
if __name__ == '__main__':
import doctest
doctest.testmod()
@cliffxuan
Copy link

elegant solution.
may I suggest you to replace parameter str with something else as it is a Python builtin function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment