[算法]最长公共子序列(python)

最长公共子序列

Description

给定两个字符串,返回两个字符串的最长公共子序列(不是最长公共子字符串),可能是多个。

Input

输入第一行为用例个数, 每个测试用例输入为两行,一行一个字符串

Output

如果没有公共子序列,不输出,如果有多个则分为多行,按字典序排序。

Sample Input 1

1
1A2BD3G4H56JK
23EFG4I5J6K7

Sample Output 1

23G456K
23G45JK
题目解析
  1. 找到最长的公共子序列
  2. 若有多个,输出所有的公共子序列
思路解析
  1. 根据状态转移方程构建dp数组
  2. 根据dp数组递归获得所有的公共子序列
  3. 将公共子序列去重并排序
例子
```
a = "siufklhdcfkl"  
b = "sadfliudklfh"
# 行为a ,列为b
```

dp数组如下:shape(len(b)+1,len(a)+1),有b+1行,a+1列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QWyMsE1E-1573030526060)()]

如何递归?

从最后一个节点开始,观察dp数组,首先明确
满足dp[i - 1][j] == dp[i][j - 1] != dp[i][j]-1的格子才是要输出的字符,因为这个字符一定是两个数组中的相同字符.
如:
  5     4
5 6   4 5  

若 dp[i - 1][j] == dp[i][j - 1] == dp[i][j]:
如:
  6
6 6  
则向左和向上同时递归

若 dp[i - 1][j] > dp[i][j - 1]
如
  5
6 6
则向左递归

若 dp[i][j - 1] > dp[i][j] 
如
  6
5 6
则向上递归

若dp[i - 1][j] == dp[i][j - 1] != dp[i][j]:
如:
5 5
5 6 
此时满足条件,记录字符串,并则向斜上方递归
代码实现
# import numpy as np

# 打印数组结构
# def printdp(dp):
#     dp2 = np.array(dp)
#     dp2 = np.c_[["*"] + list(b), dp2]
#     print(len(dp2[0]))
#     print(len(["*", "*"] + list(a)))
#     dp2 = np.insert(dp2, 0, ["*", "*"] + list(a), axis=0)
#     print(dp2.shape)
#     print(dp2)
#     return dp2

def get_path(i, j, res, l):
    # dpp = np.array(dp) # 可以debug观察数组结构
    if dp[i][j] == 0:
        if res[::-1] != "":
            l.append(res[::-1])
        # print(res[::-1])
        return
    if dp[i - 1][j] == dp[i][j - 1]:
        if dp[i - 1][j] == dp[i][j - 1] == dp[i][j]:
            get_path(i - 1, j, res, l)
            get_path(i, j - 1, res, l)
        else:
            get_path(i - 1, j - 1, res + str(a[j - 1]), l)
    elif dp[i - 1][j] > dp[i][j - 1]:
        get_path(i - 1, j, res, l)
    else:
        get_path(i, j - 1, res, l)


if __name__ == '__main__':
    case_num = [int(x) for x in input().strip().split(" ")][0]
    for i in range(case_num):
        a = input().strip()
        b = input().strip()
        dp = [[0] * (len(a) + 1) for _ in range(len(b) + 1)]  # b+1行 a+1列,初始化为0
        # 构建dp数组
        for i in range(len(b)):
            for j in range(len(a)):
                if b[i] == a[j]:
                    dp[i + 1][j + 1] = dp[i][j] + 1
                else:
                    dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1])
        l_arr = []
        # dp2 = printdp(dp)
        # 递归寻找子序列
        get_path(len(b), len(a), "", l_arr)
        #去重
        l_arr = list(set(l_arr))
        l_arr.sort()
        if len(l_arr) == 0:
            print()
        for x in l_arr:
            print(x)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值