最长公共子序列
Description
给定两个字符串,返回两个字符串的最长公共子序列(不是最长公共子字符串),可能是多个。
Input
输入第一行为用例个数, 每个测试用例输入为两行,一行一个字符串
Output
如果没有公共子序列,不输出,如果有多个则分为多行,按字典序排序。
Sample Input 1
1
1A2BD3G4H56JK
23EFG4I5J6K7
Sample Output 1
23G456K
23G45JK
题目解析
- 找到最长的公共子序列
- 若有多个,输出所有的公共子序列
思路解析
- 根据状态转移方程构建dp数组
- 根据dp数组递归获得所有的公共子序列
- 将公共子序列去重并排序
例子
```
a = "siufklhdcfkl"
b = "sadfliudklfh"
# 行为a ,列为b
```
dp数组如下:shape(len(b)+1,len(a)+1),有b+1行,a+1列
如何递归?
从最后一个节点开始,观察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)