动态规划-最大公共子序列问题(MCS)和最长公共子串问题(LCS)

最大公共子序列问题

子序列定义

一个序列去掉任意零个或多个元素剩余的序列。

问题的形式化定义

给定两个序列 X = ( x 1 , x 2 , . . . , x m ) X=(x_1, x_2,...,x_m) X=(x1,x2,...,xm) Y = ( y 1 , y 2 , . . . , y n ) Y=(y_1, y_2,...,y_n) Y=(y1,y2,...,yn),求一个最长的公共子序列 Z = ( z 1 , z 2 , . . . , z k ) Z=(z_1, z_2,...,z_k) Z=(z1,z2,...,zk),使对于所有的 p = 1 , 2 , . . . , k p=1,2,...,k p=1,2,...,k,有 z p = x i p = y j p z_p=x_{i_p}=y_{j_p} zp=xip=yjp

第一步:定义最优解

定义 d i , j d_{i,j} di,j为序列 X [ 1 , … , i ] X[1,\dots,i] X[1,,i] Y [ 1 , … , j ] Y[1,\dots,j] Y[1,,j]的最长公共子序列长度。

第二步:建立递推关系

我们从两个序列的末尾开始向开头进行递推,这样在后续自底向上填表时就能从序列头进行填写。

如果两个序列的末尾元素是相同的,那么递推关系有三种情况:
d i , j = m a x { d i − 1 , j − 1 + 1 d i − 1 , j d i , j − 1 ( X [ i ] = Y [ j ] ) d_{i,j}=max\begin{cases}d_{i-1,j-1}+1\\d_{i-1,j}\\d_{i,j-1}\\\end{cases}( X[i]=Y[j]) di,j=max di1,j1+1di1,jdi,j1(X[i]=Y[j])
事实上, d i − 1 , j d_{i-1,j} di1,j d i , j − 1 d_{i,j-1} di,j1至多比 d i − 1 , j − 1 d_{i-1,j-1} di1,j1大1。因此递推中第一种可能一定大于等于第二、第三种可能。可以仅保留第一项:
d i , j = d i − 1 , j − 1 + 1 ( X [ i ] = Y [ j ] ) d_{i,j}=d_{i-1,j-1}+1 (X[i]=Y[j]) di,j=di1,j1+1(X[i]=Y[j])
即相同的最后元素一定要被加入最长的公共子序列中。

如果两个序列的末尾元素是不同的,递推关系无非两种情况:
d i , j = m a x { d i − 1 , j d i , j − 1 ( X [ i ] ≠ Y [ j ] ) d_{i,j}=max \begin{cases}d_{i-1,j}\\d_{i,j-1}\\\end{cases} (X[i]\neq Y[j]) di,j=max{di1,jdi,j1(X[i]=Y[j])

综上所述,递推关系为:
d i , j = { d i − 1 , j − 1 + 1 ( X [ i ] = Y [ j ] ) m a x { d i , j − 1 , d i − 1 , j } ( X [ i ] ≠ Y [ j ] ) d_{i,j}=\begin{cases} d_{i-1,j-1}+1(X[i]= Y[j])\\ max\{d_{i,j-1}, d_{i-1,j}\}(X[i]\neq Y[j])\\ \end{cases} di,j={di1,j1+1(X[i]=Y[j])max{di,j1,di1,j}(X[i]=Y[j])

第三步:自底向上计算(填表)与最优方案追踪

X = A B C B D A B X=ABCBDAB X=ABCBDAB Y = B D C A B A Y=BDCABA Y=BDCABA为例。
为了追踪最优方案,应该记录每次递推时是从 d i − 1 , j − 1 d_{i-1,j-1} di1,j1, d i − 1 , j − 1 d_{i-1,j-1} di1,j1还是 d i , j − 1 d_{i,j-1} di,j1得来,分别采取的操作是什么。建立追踪表,如果是从左上角推得(即元素相等),填写LU;如果是从左边推得,填写L;如果是从上面推得,填写U

Step 0

初始化表格:

0123456
00000000
10
20
30
40
50
60
70

追踪表:

0123456
0xxxxxxx
1x
2x
3x
4x
5x
6x
7x
Step 1

因为 X [ 1 ] ≠ Y [ 1 ] X[1]\neq Y[1] X[1]=Y[1] d 1 , 1 = m a x { d 0 , 1 , d 1 , 0 } = 0 d_{1,1}=max\{d_{0,1},d_{1,0}\}=0 d1,1=max{d0,1,d1,0}=0

0123456
00000000
100
20
30
40
50
60
70

追踪表:
当上面和左边的值相等时,不妨记为从上面推得。

0123456
0xxxxxxx
1xU
2x
3x
4x
5x
6x
7x

Step N

因为 X [ 7 ] ≠ Y [ 6 ] X[7]\neq Y[6] X[7]=Y[6] d 7 , 6 = m a x { d 7 , 5 , d 6 , 6 } = 0 d_{7,6}=max\{d_{7,5},d_{6,6}\}=0 d7,6=max{d7,5,d6,6}=0

0123456
00000000
10000111
20111122
30112222
40112233
50122233
60122334
70122344

追踪表:

0123456
0xxxxxxx
1xUUULULLU
2xLULLULUL
3xUULULUU
4xLUUUULUL
5xULUUUUU
6xUUULUULU
7xLUUUULUU

d 7 , 6 d_{7,6} d7,6倒推得到的序列为:LU,L,LU,L,LU,U,LU,U,其中LU符号代表这个元素在公共子序列中。
X X X Y Y Y的4个公共子序列元素为: X [ 2 ] X[2] X[2] X [ 3 ] X[3] X[3] X [ 4 ] X[4] X[4] X [ 6 ] X[6] X[6]。即 B C B A BCBA BCBA

最长公共子串问题

子串定义

给定串中零个或多个连续的元素(如字符)组成的子序列。

问题的形式化定义

给定两个串 X = x 1 x 2 . . . x m X=x_1x_2...x_m X=x1x2...xm Y = y 1 y 2 . . . y n Y=y_1y_2...y_n Y=y1y2...yn,求一个最长的公共子串 Z = z 1 z 2 . . . z k Z=z_1 z_2...z_k Z=z1z2...zk,使得存在下标 i i i j j j使得 x i x i + 1 . . . x i + k − 1 x_ix_{i+1}...x_{i+k-1} xixi+1...xi+k1= y j y j + 1 . . . y j + k − 1 y_jy_{j+1}...y_{j+k-1} yjyj+1...yj+k1

第一步:定义最优解

定义 d i , j d_{i,j} di,j为串 X [ 1 , … , i ] X[1,\dots,i] X[1,,i] X [ i ] X[i] X[i]结尾的 Y [ 1 , … , j ] Y[1,\dots,j] Y[1,,j] Y [ j ] Y[j] Y[j]结尾的最长的公共子串的长度。注意这样定义的 d m , n d_{m,n} dm,n一般不是我们需要的解的长度,我们最终要求的最优解的长度应该是 m a x { d i , j } max\{d_{i,j}\} max{di,j}。这里是为了便于建立递推关系,用到一种添加约束的小技巧,而不是直接去定义最优解的结构。

第二步:建立递推关系

如果两个串的末尾元素是相同的,那么递推关系就是:
d i , j = d i − 1 , j − 1 + 1 ( X [ i ] = Y [ j ] ) d_{i,j}=d_{i-1,j-1}+1(X[i]=Y[j]) di,j=di1,j1+1(X[i]=Y[j])
如果两个串的末尾元素不相同,递推终止:
d i , j = 0 ( X [ i ] ≠ Y [ j ] ) d_{i,j}=0 (X[i]\neq Y[j]) di,j=0(X[i]=Y[j])
综上所述,递推关系为:
d i , j = { d i , j = d i − 1 , j − 1 + 1 ( X [ i ] = Y [ j ] ) d i , j = 0 ( X [ i ] ≠ Y [ j ] ) d_{i,j}=\begin{cases} d_{i,j}=d_{i-1,j-1}+1(X[i]=Y[j])\\ d_{i,j}=0 (X[i]\neq Y[j])\\ \end{cases} di,j={di,j=di1,j1+1(X[i]=Y[j])di,j=0(X[i]=Y[j])

第三步:自底向上计算(填表)与最优方案追踪

X = D E A D B E E X=DEADBEE X=DEADBEE Y = E A T B E E Y=EATBEE Y=EATBEE为例。
本问题的递推关系比较简单,无需另外建立追踪表。只需要维护一个记录最长公共子串长度的全局变量 l m a x l_{max} lmax和记录最长公共子串在 X X X串的结束位置的全局变量 p m a x p_{max} pmax即可。

Step 0

初始化表格:

0123456
00000000
10
20
30
40
50
60
70

l m a x = 0 l_{max}=0 lmax=0, p m a x = 0 p_{max}=0 pmax=0

Step 1

因为 X [ 1 ] ≠ Y [ 1 ] X[1]\neq Y[1] X[1]=Y[1] d 1 , 1 = 0 d_{1,1}=0 d1,1=0

0123456
00000000
100
20
30
40
50
60
70

l m a x = 0 l_{max}=0 lmax=0, p m a x = 0 p_{max}=0 pmax=0

Step N

因为 X [ 7 ] = Y [ 6 ] X[7]= Y[6] X[7]=Y[6] d 7 , 6 = d 6 , 5 + 1 = 3 d_{7,6}=d_{6,5}+1=3 d7,6=d6,5+1=3

0123456
00000000
10000000
20100011
30020000
40000000
50000100
60100021
70100013

l m a x = 3 l_{max}=3 lmax=3, p m a x = 7 p_{max}=7 pmax=7

故最长公共子串的长度为3,其在 X X X串中的结束位置为7。得最长公共子串为 X [ 5 , 6 , 7 ] = B E E X[5,6,7]=BEE X[5,6,7]=BEE

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值