【动态规划】最长公共子序列Python实现
问题描述
- 给定两个序列 X = { ? x 1 , x 2 , ? ? , x m ? } X = \set{x_{1} , x_{2} , \cdots , x_{m}} X={x1?,x2?,?,xm?}和 Y = { ? y 1 , y 2 , ? ? , y n ? } Y = \set{y_{1} , y_{2} , \cdots , y_{n}} Y={y1?,y2?,?,yn?},找出 X X X和 Y Y Y的最长公共子序列
最长公共子序列的结构
- 设序列
X
=
{
?
x
1
,
x
2
,
?
?
,
x
m
?
}
X = \set{x_{1} , x_{2} , \cdots , x_{m}}
X={x1?,x2?,?,xm?}和
Y
=
{
?
y
1
,
y
2
,
?
?
,
y
n
?
}
Y = \set{y_{1} , y_{2} , \cdots , y_{n}}
Y={y1?,y2?,?,yn?}的最长公共子序列为
Z
=
{
?
z
1
,
z
2
,
?
?
,
z
k
?
}
Z = \set{z_{1} , z_{2} , \cdots , z_{k}}
Z={z1?,z2?,?,zk?}
- 若 x m = y n x_{m} = y_{n} xm?=yn?,则 z k = x m = y n z_{k} = x_{m} = y_{n} zk?=xm?=yn?,且 Z k ? 1 Z_{k - 1} Zk?1?是 X m ? 1 X_{m - 1} Xm?1?和 Y n ? 1 Y_{n - 1} Yn?1?的最长公共子序列
- 若 x m ≠ y n x_{m} \neq y_{n} xm?=yn?且 z k ≠ x m z_{k} \neq x_{m} zk?=xm?,则 Z Z Z是 X m ? 1 X_{m - 1} Xm?1?和 Y Y Y的最长公共子序列
- 若 x m ≠ y n x_{m} \neq y_{n} xm?=yn?且 z k ≠ y n z_{k} \neq y_{n} zk?=yn?,则 Z Z Z是 X X X和 Y n ? 1 Y_{n - 1} Yn?1?的最长公共子序列
子问题的递归结构
- 当 x m = y n x_{m} = y_{n} xm?=yn?时,找出 X m ? 1 X_{m - 1} Xm?1?和 Y n ? 1 Y_{n - 1} Yn?1?的最长公共子序列,然后在其尾部加上 x m x_{m} xm?
- 当 x m ≠ y n x_{m} \neq y_{n} xm?=yn?时,找出 X m ? 1 X_{m - 1} Xm?1?和 Y Y Y的一个最长公共子序列及 X X X和 Y n ? 1 Y_{n - 1} Yn?1?的一个最长公共子序列,这两个公共子序列中较长者即为 X X X和 Y Y Y的最长公共子序列
c [ i ] [ j ] c[i][j] c[i][j]递归方程
c [ i ] [ j ] = { 0 , i = 0 , j = 0 c [ i ? 1 ] [ j ? 1 ] + 1 , i , j > 0 ; x i = y j max ? { ? c [ i ] [ j ? 1 ] , c [ i ? 1 ] [ j ] ? } , i , j > 0 ; x i ≠ y j c[i][j] = \begin{cases} 0 , & i = 0 , j = 0 \\ c[i - 1][j - 1] + 1 , & i , j > 0 ; x_{i} = y_{j} \\ \max\set{c[i][j - 1] , c[i - 1][j]} , & i , j > 0 ; x_{i} \neq y_{j} \end{cases} c[i][j]=? ? ??0,c[i?1][j?1]+1,max{c[i][j?1],c[i?1][j]},?i=0,j=0i,j>0;xi?=yj?i,j>0;xi?=yj??
时间复杂性
- 由于每个数组单元的计算耗费 O ( 1 ) O(1) O(1)时间,因此算法耗时 O ( m n ) O(mn) O(mn)
构造最长公共子序列
- 在算法 L C S LCS LCS中,每次递归调用使 i i i或 j j j减 1 1 1,因此算法的计算时间为 O ( m + n ) O(m + n) O(m+n)
Python
实现
def longest_common_subsequence(str_1, str_2):
m = len(str_1)
n = len(str_2)
# 创建一个二维数组来存储子问题的解
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 填充二维数组
for i in range(1, m + 1):
for j in range(1, n + 1):
if str_1[i - 1] == str_2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
# 构造最长公共子序列
lcs = ''
i, j = m, n
while i > 0 and j > 0:
if str_1[i - 1] == str_2[j - 1]:
lcs = str_1[i - 1] + lcs
i -= 1
j -= 1
elif dp[i - 1][j] > dp[i][j - 1]:
i -= 1
else:
j -= 1
return lcs
str_1 = 'ABCDGH'
str_2 = 'AEDFHR'
lcs = longest_common_subsequence(str_1, str_2)
print(f'最长公共子序列: {lcs}')
算法的改进
- 如果只需要计算最长公共子序列的长度,则只用到数组 c c c的第 i i i行和第 i ? 1 i - 1 i?1行
- 因此,用两行的数组空间就可以计算出最长公共子序列的长度,可将空间需求减至 O ( min ? { ? m , n ? } ) O(\min\set{m , n}) O(min{m,n})
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!