Reputation: 21353
I have written code to compute the longest palindrome subsequence.
def longest_pal_subseq(sequence, i, j):
if (i == j):
return 1
if (sequence[i] == sequence[j] and j - i == 1):
return 2
if (sequence[i] == sequence[j]):
return longest_pal_subseq(sequence, i + 1, j - 1) + 2
return max(longest_pal_subseq(sequence, i, j - 1),
longest_pal_subseq(sequence, i + 1, j))
if __name__ == '__main__':
sequence = "abracadabra"
print("Length: ", longest_pal_subseq(sequence, 0, len(sequence) - 1))
But how can the code be modified to also print the subsequence it has found of that length?
Upvotes: 0
Views: 154
Reputation: 3934
Edit: The original answer had a bug.
This is fastest and easiest to follow but doesn't use dynamic programming.
It only returns contiguous palindromes not as required by the question.
def is_pal(string):
return string == string[:: -1]
def longest_pal(string):
full = len(string)
for length in range(full, -1, -1):
for i in range(full-length+1):
if is_pal(string[i:length+i]):
return length, i, string[i:length+i]
longest_pal('abracadabra')
# (3, 3, 'aca')
I can see 2 palindromes of length 3 'aca' and 'ada' but none of length 7.
A second approach uses a dictionary res_so_far
to store the sequences with their longest palindromic subsequence. If the same sequence is processed more than once the result is returned from the dictionary without running the rest of the recursive function calls. This returns non contiguous palindromic sequences in the base sequence.
def longest(a, b):
if len(a) >= len(b): return a
return b
def longest_pal_in( sequence ):
res_so_far = {} # To store intermediate results as they are found.
def set(seq, res):
""" set result so far then return result.
This is incorporated in each return statement in longest_pal
"""
res_so_far[seq] = res
return res
def longest_pal( sequence ):
try:
return res_so_far[sequence]
# If sequence is stored in res_so_far return the result
except KeyError: # If not run the rest of the function
pass
if len( sequence ) < 4:
if sequence[0] == sequence[-1]: # len 1,2, or 3 if first == last then palindrome
return set(sequence, sequence)
elif sequence[0] == sequence[-1]:
# Changed to find sub sequences, not substrings
return set(sequence, sequence[0]+longest_pal( sequence[1:-1] )+sequence[-1] )
# Three lines below removed to return non contiguous sub sequences.
# sub = longest_pal( sequence[1:-1] )
# if len(sub) == (len(sequence) - 2):
# return set(sequence, sequence)
return set(sequence, longest(longest_pal( sequence[:-1] ), longest_pal( sequence[1:] )))
return longest_pal(sequence)
Timings
test = 'abracadabra'
%timeit longest_pal(test) # Not recursive
# 54.4 µs ± 765 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit longest_pal_in(test) # Recursive
# 93.5 µs ± 2.95 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit longest_pal_in(test+test)
# 369 µs ± 5.62 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit longest_pal_in(test+test+test)
# 784 µs ± 39.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
longest_pal_in performs roughly O(n^2)
HTH
Upvotes: 1
Reputation: 12666
You could return a dictionary with the palindrome and length.
return {"length": max(longest_pal_subseq(sequence, i, j - 1),
longest_pal_subseq(sequence, i + 1, j)),
"palindrome": sequence}
You'd have to alter the longest_pal_subseq
to use the dict throughout as well.
Upvotes: 0