I solved a puzzle but need to optimize my solution. The puzzle says that I am to take a string S, find all permutations of its characters, sort my results, and then return the one-based index of where S appears in that list.
For example, the string 'bac' appears in the 3rd position in the sorted list of its own permutations: ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']
.
My problem is that the puzzle limits my execution time to 500ms. One of the test cases passed "BOOKKEEPER" as an input, which takes ~4.2s for me to complete.
I took a (possibly naive) dynamic programming approach using memoization using a dict keyed by one particular permutation of some character set, but that's not enough.
What is my bottleneck?
I'm profiling in the meantime to see if I can answer my own question, but I invite those who see the problem outright to help me understand how I slowed this down.
EDIT: My solution appears to outperform itertools.permutations
. 10+ seconds for input "QUESTION". But to be fair, this includes time printing so this might not be a fair comparison. Even so, I'd rather submit a handcoded solution with competitive performance knowing why mine was worse than to opt for a module.
memo = {}
def hash(word):
return ''.join(sorted(word))
def memoize(word, perms):
memo[hash(word)] = perms
return perms
def permutations(word, prefix = None):
"""Return list of all possible permutatons of given characters"""
H = hash(word)
if H in memo:
return [s if prefix is None else prefix + s for s in memo[H]]
L = len(word)
if L == 1:
return [word] if prefix is None else [prefix + word]
elif L == 2:
a = word[0] + word[1]
b = word[1] + word[0]
memoize(word, [a, b])
if prefix is not None:
a = prefix + a
b = prefix + b
return [a, b]
perms = []
for i in range(len(word)):
perms = perms + permutations(word[:i] + word[i+1:], word[i])
memoize(word, perms)
return [prefix + s for s in perms] if prefix is not None else perms
def listPosition(word):
"""Return the anagram list position of the word"""
return sorted(list(set(permutations(word)))).index(word) + 1
print listPosition('AANZ')