Longest Palindrome Subsequence,最长回文子序列(LPS)

Last Updated: 2024-03-27 07:36:29 Wednesday

-- TOC --

LPS其实就是反过来的LCS问题

将一个序列,与它自己的逆序,求LCS,得到的就是这个序列的LPS。不管是从左到右,还是从右到左,LPS都是子序列。

我自己是写了一段dummy代码之后,才认识到这个事实的。

def get_lcs_bu(a, b):
    alen = len(a)
    blen = len(b)
    pos = [['^']*blen for _ in range(alen)]
    rec = [0] * (blen+1)
    for i in range(alen):
        c = rec[:]
        for j in range(blen):
            if a[i] == b[j]:
                rec[j+1] = c[j] + 1
                pos[i][j] = '\\'
            else:
                if rec[j] >= c[j+1]:
                    rec[j+1] = rec[j]
                else:
                    rec[j+1] = c[j+1]
                    pos[i][j] = '<'
    #
    r = rec[j+1]
    lcs = ''
    i = alen - 1
    j = blen - 1
    while not (i<0 or j<0):
        if pos[i][j] == '\\':
            lcs += str(a[i])
            i -= 1
            j -= 1
        elif pos[i][j] == '^':
            j -= 1
        else:  # '<'
            i -= 1
    return r, lcs[::-1]


class palindrome:  # dummy code

    def __init__(self, s):
        self.s = s
        self.slen = len(s)
        self.rec = {}

    def _rlcs(self, p, q):  # include q, but not p
        if p<=0 or q>=self.slen:
            return 0
        if (p,q) in self.rec:
            return self.rec[p,q]
        r = 0
        for i in reversed(range(p)):
            for j in range(q,self.slen):
                if self.s[i] == self.s[j]:
                    r = max(r, 2+self._rlcs(i,j+1))
                    break
        self.rec[p,q] = r
        return r

    def mps(self):
        ans = 0
        for i in range(self.slen):
            while i < self.slen:
                r = 1
                j = i+1
                while j<self.slen and self.s[j]==self.s[i]:
                    j += 1
                    r += 1
                r += self._rlcs(i,j)
                ans = max(ans, r)
                i += 1
        return ans


a = 'banana'
pa = palindrome(a)
print(pa.mps())
print(get_lcs_bu(a,a[::-1]))

a = 'abc123c4b5a'
pa = palindrome(a)
print(pa.mps())
print(get_lcs_bu(a,a[::-1]))

a = 'abc66666a'
pa = palindrome(a)
print(pa.mps())
print(get_lcs_bu(a,a[::-1]))

a = 'geeksforgeeks'
pa = palindrome(a)
print(pa.mps())
print(get_lcs_bu(a,a[::-1]))

输出:

5
(5, 'anana')
7
(7, 'abc3cba')
7
(7, 'a66666a')
5
(5, 'eegee')

还是写一个LPS问题的接口吧,LCS和LPS问题最精简完美的版本:

def lcs_bu(a, b):
    alen = len(a)
    blen = len(b)
    rec = [0] * (blen+1)
    for i in range(alen):
        for j in range(blen):
            if a[i] == b[j]:
                rec[j+1] = rec[j] + 1
            else:
                rec[j+1] = max(rec[j], rec[j+1])
    return rec[blen]


def lps_bu(s):
    n = len(s)
    rec = [0] * (n+1)
    for i in reversed(range(n)):
        for j in range(n):
            if s[j] == s[i]:
                rec[j+1] = rec[j] + 1
            else:
                rec[j+1] = max(rec[j], rec[j+1])
    return rec[n]


for a in ('abc12333cba', 'geeks44geeks', '12333'):
    print(lcs_bu(a,a[::-1]))
    print(lps_bu(a))

本文链接:https://cs.pynote.net/ag/dp/202403251/

-- EOF --

-- MORE --