(编辑距离)为了将一个文本串
x
[
1..
m
]
x[1..m]
x[1..m]转换为目标串
y
[
1..
n
]
y[1..n]
y[1..n],我们可以使用多种变换操作。我们的目标是,给定
x
x
x和
y
y
y,求将
x
x
x转换为
y
y
y的一个变换操作序列。我们使用一个数组
z
z
z保存中间结果,假定它足够大,可存下中间结果的所有字符。初始时,
z
z
z是空的;结束时,应有
z
[
j
]
=
y
[
j
]
,
j
=
1
,
2
,
…
,
n
z[j] = y[j],j = 1, 2, …, n
z[j]=y[j],j=1,2,…,n。我们维护两个下标
i
i
i和
j
j
j,分别指向
x
x
x和
y
y
y中的位置,变换操作允许改变
z
z
z的内容和这两个下标。初始时,
i
=
j
=
1
i = j = 1
i=j=1。在转换过程中应处理
x
x
x的所有字符,这意味着在变换操作结束时,应当有
i
=
m
+
1
i = m+1
i=m+1。
我们可以使用如下6种变换操作:
复制(copy) — 从
x
x
x复制当前字符到
z
z
z,即进行赋值
z
[
j
]
=
x
[
i
]
z[j] = x[i]
z[j]=x[i],并将两个下标
i
i
i和
j
j
j都增1。此操作处理了
x
[
i
]
x[i]
x[i]。
替换(replace) — 将
x
x
x的当前字符
x
[
i
]
x[i]
x[i]替换为另一个字符
c
c
c,然后进行赋值
z
[
j
]
=
c
z[j] = c
z[j]=c,并将两个下标
i
i
i和
j
j
j都增1。此操作处理了
x
[
i
]
x[i]
x[i]。
删除(delete) — 删除
x
x
x中的当前字符
x
[
i
]
x[i]
x[i],即将
i
i
i增1,
j
j
j不变。此操作处理了
x
[
i
]
x[i]
x[i]。
插入(insert) — 将字符
c
c
c插入
z
z
z中,
z
[
j
]
=
c
z[j] = c
z[j]=c,将
j
j
j增1,
i
i
i不变。此操作未处理
x
x
x中的字符。
旋转(twiddle,即交换) — 将
x
x
x中的当前字符和下一个字符复制到
z
z
z中,但交换顺序,
z
[
j
]
=
x
[
i
+
1
]
z[j] = x[i+1]
z[j]=x[i+1]且
z
[
j
+
1
]
=
x
[
i
]
z[j+1] = x[i]
z[j+1]=x[i],将
i
i
i和
j
j
j都增2。此操作处理了
x
[
i
]
x[i]
x[i]和
x
[
i
+
1
]
x[i+1]
x[i+1]。
终止(kill) — 删除
x
x
x中的剩余字符,令
i
=
m
+
1
i = m+1
i=m+1。此操作处理了
x
x
x中所有尚未处理的字符。如果执行此操作,则转换过程结束。
下面给出了将源字符串algorithm转换为目标字符串altruistic的一种变换操作序列,下划线指出执行一个变换操作后两个下标i和j的位置:
注意,还有其他方法将algorithm转换为altruistic。
每个变换操作都有相应的代价。具体的代价依赖于特定的应用,但我们假定每个操作的代价是一个已知的常量。我们还假定复制和替换的代价小于删除和插入的组合代价,否则复制和替换操作就没有意义了。一个给定的变换操作序列的代价为其中所有变换操作的代价之和。在上例中,将algorithm转换为altruistic的代价为
3
•
c
o
s
t
(
c
o
p
y
)
+
c
o
s
t
(
r
e
p
l
a
c
e
)
+
c
o
s
t
(
d
e
l
e
t
e
)
+
4
•
c
o
s
t
(
i
n
s
e
r
t
)
+
c
o
s
t
(
t
w
i
d
d
l
e
)
+
c
o
s
t
(
k
i
l
l
)
3•cost(copy) + cost(replace) + cost(delete) + 4•cost(insert) + cost(twiddle) + cost(kill)
3•cost(copy)+cost(replace)+cost(delete)+4•cost(insert)+cost(twiddle)+cost(kill)
a. 给定两个字符串
x
[
1..
m
]
x[1..m]
x[1..m]和
y
[
1..
n
]
y[1..n]
y[1..n]以及变换操作的代价,
x
x
x和
y
y
y的编辑距离(edit distance)是将
x
x
x转换为
y
y
y的最小代价的变换操作序列的代价值。设计动态规划算法,求
x
[
1..
m
]
x[1..m]
x[1..m]到
y
[
1..
n
]
y[1..n]
y[1..n]的编辑距离,并打印最优变换操作序列。分析算法的时间和空间复杂度。
编辑距离问题是DNA序列对齐问题的推广。已有多种方法可以通过对齐两个DNA序列来衡量它们的相似度。有一种对齐方法是将空格符插入到两个序列
x
x
x和
y
y
y中,可以插入到任何位置(包括两端),使得结果序列
x
’
x’
x’和
y
’
y’
y’具有相同的长度,但不会在相同的位置出现空格符(即不存在
j
j
j,使得
x
’
[
j
]
x’[j]
x’[j]和
y
’
[
j
]
y’[j]
y’[j]都是空格符)。然后为每个位置“打分”,位置j的分数为:
•
+
1
+1
+1,如果
x
’
[
j
]
=
y
’
[
j
]
x’[j] = y’[j]
x’[j]=y’[j]且不是空格符
•
−
1
-1
−1,如果
x
’
[
j
]
≠
y
’
[
j
]
x’[j] ≠ y’[j]
x’[j]̸=y’[j]且都不是空格符
•
−
2
-2
−2,如果
x
’
[
j
]
x’[j]
x’[j]或
y
’
[
j
]
y’[j]
y’[j]是空格符
对齐方案的分数为每个位置的分数之和。例如,给定序列
x
x
x = GATCGGCAT和
y
y
y = CAATGTGAATC,一种对齐方案为
+表示该位置分数为+1,-表示分数为-1,*表示分数为-2,因此此方案的总分数为6•1 - 2•1 - 4•2 = -4。
b. 解释如何将最优对齐问题转换为编辑距离问题,使用的操作为变换操作复制、替换、删除、插入、旋转和终止的子集。
解
a.
假设将
x
[
1..
i
]
(
0
≤
i
≤
m
)
x[1..i] (0 ≤ i ≤ m)
x[1..i](0≤i≤m)转换为
y
[
1..
j
]
(
0
≤
j
≤
n
)
y[1..j] (0 ≤ j ≤ n)
y[1..j](0≤j≤n)的最优代价为
c
[
i
,
j
]
c[i, j]
c[i,j]。注意,
x
[
1..0
]
x[1..0]
x[1..0]和
y
[
1..0
]
y[1..0]
y[1..0]表示的是空字符串。
1. 如果
i
>
0
i > 0
i>0并且
j
>
0
j > 0
j>0
考虑
x
[
1..
i
]
→
y
[
1..
j
]
x[1..i] → y[1..j]
x[1..i]→y[1..j]的转换过程的最后一步操作,有6种可能的选择。注意,有的操作只能在满足条件的情况下才能执行。
1) copy
2) replace
我们有必要假设replace的代价大于copy的代价,否则完全可以用replace代替copy,而copy根本不会被用到。
3) delete
4) insert
5) twiddle
6) kill
kill只能为最后一步操作,因此必然有
i
=
m
i = m
i=m和
j
=
n
j = n
j=n;并且kill操作前的转换为
x
[
1..
k
]
→
y
[
1..
n
]
x[1..k] → y[1..n]
x[1..k]→y[1..n],其中
0
≤
k
<
m
0 ≤ k < m
0≤k<m。注意,如果执行kill操作,需要记录源字符串
x
x
x此时已处理的字符个数。
分别计算以上6种情况的处理代价,选取其中最小者作为最优代价,即
2. 如果
i
=
0
i = 0
i=0或者
j
=
0
j = 0
j=0
1)
i
=
0
i = 0
i=0,即
x
[
1..0
]
→
y
[
1..
j
]
x[1..0] → y[1..j]
x[1..0]→y[1..j]
由于
x
[
1..0
]
x[1..0]
x[1..0]为空,故只能进行
j
j
j步insert,才能得到
y
[
1..
j
]
y[1..j]
y[1..j]。
2)
j
=
0
j = 0
j=0,即
x
[
1..
i
]
→
y
[
1..0
]
x[1..i] → y[1..0]
x[1..i]→y[1..0]
由于
y
[
1..0
]
y[1..0]
y[1..0]为空,如果
i
<
m
i < m
i<m或者
n
>
0
n > 0
n>0,只能进行
i
i
i步delete操作;如果
i
=
m
i = m
i=m并且
n
=
0
n = 0
n=0,可以通过kill操作一步到位,也可以进行
m
m
m步delete操作,取二者中cost较小者。
可以用一个数组
o
p
[
i
,
j
]
op[i, j]
op[i,j]来记录
x
[
1..
i
]
→
y
[
1..
j
]
x[1.. i] → y[1.. j]
x[1..i]→y[1..j]的最后一步操作。
不难得出,该算法的时间复杂度和空间复杂度都为
O
(
m
n
)
O(mn)
O(mn)。
b.
DNA对齐问题可通过下面的方法转换成编辑距离问题:
1)
x
’
[
j
]
=
y
’
[
j
]
x’[j] = y’[j]
x’[j]=y’[j]
↔
\leftrightarrow
↔ 相当于copy操作,对应的分数为
s
c
o
r
e
(
c
o
p
y
)
=
+
1
score(copy) = +1
score(copy)=+1
2)
x
’
[
j
]
≠
y
’
[
j
]
x’[j] ≠ y’[j]
x’[j]̸=y’[j]且都不是空格
↔
\leftrightarrow
↔ 相当于replace操作,对应的分数为
s
c
o
r
e
(
r
e
p
l
a
c
e
)
=
−
1
score(replace) = -1
score(replace)=−1
3)
y
’
[
j
]
=
y’[j] =
y’[j]= 空格
↔
\leftrightarrow
↔ 相当于delete操作,对应的分数为
s
c
o
r
e
(
d
e
l
e
t
e
)
=
−
2
score(delete) = -2
score(delete)=−2
4)
x
’
[
j
]
=
x’[j] =
x’[j]= 空格
↔
\leftrightarrow
↔ 相当于insert操作,对应的分数为
s
c
o
r
e
(
i
n
s
e
r
t
)
=
−
2
score(insert) = -2
score(insert)=−2
twiddle和kill操作不能使用。注意,编辑距离问题是求解最小化代价,而DNA匹配问题是求解最大化分数。
本节相关的code链接。
https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter15/Problems/Problem_15-5