简体   繁体   中英

How to write a function to find the longest common subsequence using dynamic programming?

To be clear I am looking for the subsequence itself and not the length. I have written this function which works the majority of the time but in some cases it doesn't work. I have to write this recursively without any loops or imports. I used a memoise function to be more efficient but didn't include it here.

This function works when s1 = "abcde" and s2 = "qbxxd" (which correctly returns "bd") but it doesn't work for when s1 = "Look at me, I can fly!" and s2 = "Look at that, it's a fly" which should return "Look at , a fly" but I get instead "Look at a fly". For whatever reason the comma and the space is ignored. I've tried s1 = "ab, cde" and s2 = "qbxx, d" which correctly returns "b, d".

def lcs(s1, s2):
"""y5tgr"""
i = len(s1)
j = len(s2)
if i == 0 or j == 0:
    return ""
if s1[i-1] == s2[j-1]:
    return lcs(s1[:-1], s2[:-1]) + s1[i-1]
else:
    return max(lcs(s1[:-1], s2), lcs(s1, s2[:-1]))

I have a feeling the problem is with the last line and the max function. I've seen solutions with for and while loops but not without.

There's only a slight change to fix your code (you're right the problem was in max).

Just change max so it finds the string of max length using it's key function.

def lcs(s1, s2):
    """y5tgr"""
    i = len(s1)
    j = len(s2)
    if i == 0 or j == 0:
        return ""
    if s1[i-1] == s2[j-1]:
        return lcs(s1[:-1], s2[:-1]) + s1[i-1]
    else:
        # Find max based upon the string length
        return max(lcs(s1[:-1], s2), lcs(s1, s2[:-1]), key=len)

However, this is very slow without memoization

Code with Memoization (to improve performance)

Memoization Decorator Reference

import functools

def memoize(obj):
    cache = obj.cache = {}

    @functools.wraps(obj)
    def memoizer(*args, **kwargs):
        if args not in cache:
            cache[args] = obj(*args, **kwargs)
        return cache[args]
    return memoizer

@memoize
def lcs(s1, s2):
    """y5tgr"""
    i = len(s1)
    j = len(s2)
    if i == 0 or j == 0:
        return ""
    if s1[i-1] == s2[j-1]:
        return lcs(s1[:-1], s2[:-1]) + s1[i-1]
    else:
        return max(lcs(s1[:-1], s2), lcs(s1, s2[:-1]), key=len)

Test

s1 = "Look at me, I can fly!"
s2 = "Look at that, it's a fly"
print(lcs(s1, s2))

Output

Look at ,  a fly

For strings, max takes the string which lexicographically goes last:

>>> max("a", "b")
'b'
>>> max("aaaaa", "b")
'b'
>>> 

Certainly not what you need; you seem to look for the longer of the two. You don't need a loop, just a comparison:

lsc1 = lcs(s1[:-1], s2)
lcs2 = lcs(s1, s2[:-1])
return lcs1 if len(lcs1) > len(lcs2) else lcs2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM