Algorithm 动态规划:最长公共子序列

Algorithm 动态规划:最长公共子序列,algorithm,dynamic-programming,Algorithm,Dynamic Programming,我将复习在寻找两个等长字符串的最长公共子序列的上下文中讨论动态规划的笔记。该算法输出长度(而不是子字符串) 所以我有两个字符串,比如: S=ABAZDC,T=BACBAD 最长的公共子序列是ABAD(子字符串不必是相邻的字母) 算法如下,其中LCS[i,j]表示S[1..i]和T[1..j]的最长公共子序列: if S[i] != T[j] then LCS[i, j] = max(LCS[i - 1, j], LCS[i, j - 1]) else LCS[i, j] = 1

我将复习在寻找两个等长字符串的最长公共子序列的上下文中讨论动态规划的笔记。该算法输出长度(而不是子字符串)

所以我有两个字符串,比如:

S=ABAZDC,T=BACBAD

最长的公共子序列是ABAD(子字符串不必是相邻的字母)

算法如下,其中LCS[i,j]表示S[1..i]和T[1..j]的最长公共子序列:

if S[i] != T[j] then
    LCS[i, j] = max(LCS[i - 1, j], LCS[i, j - 1])
else
    LCS[i, j] = 1 + LCS[i - 1, j - 1]
我的笔记声称你可以填写一个表格,每个字符串都是沿着一个轴写的。比如:

B A C B A D

零一一

B 1 1 2 2 2

A

Z

D

C

两个问题:

1) 我们如何开始填写这个表格呢。算法是递归的,但似乎没有提供基本情况(否则我只调用LCS[5,5])?Notes声称你可以用i和j做两个简单的循环,并在固定时间内填充每个点

2) 我们如何修改算法,使最长的公共子序列是相邻字母的序列?我的想法是,一旦我发现S中的下一个字母与t中的下一个字母不匹配,我就必须将当前子序列的长度重置为0。但这很棘手,因为我想跟踪到目前为止看到的最长的子序列(我看到的第一个子序列可能是最长的)。所以,也许我会有一个额外的参数,longestshusfar,当我们最初调用算法并在随后的调用中进行更改时,它是0

有人能把这更严格一点吗


谢谢

首先,算法是递归的,但实现总是迭代的。换句话说,我们不显式地从函数本身调用相同的函数(递归)

我们使用已经填充的表项来补偿递归

比如说,您有两条长度为M的字符串

for(i = 0 to M)
{
  LCS[0][i]=0;
}
for(i = 1 to M)
{
  LCS[i][0]=0;
}
然后定义一个维度(M+1)X(M+1)的表格

for(i = 0 to M)
{
  LCS[0][i]=0;
}
for(i = 1 to M)
{
  LCS[i][0]=0;
}
你有一张像这样的桌子

    B,A,C,B,A,D
  0,0,0,0,0,0,0
A 0
B 0
A 0
Z 0
D 0
C 0
0th col中的每个零表示,如果未考虑字符串BACBAD的任何字符,则LCS的长度=0

第0行0中的每个零表示如果未考虑字符串ABAZDC的字符,则LCS的长度=0

其余条目使用您提到的规则填充

for(i = 1 to M)
{
 for(j = 1 to M)
 {
  if S[i-1] != T[j-1] then
    LCS[i, j] = max(LCS[i - 1, j], LCS[i, j - 1])
  else
    LCS[i, j] = 1 + LCS[i - 1, j - 1]
 }
}
注意它的S[i-1]!=T[j-1]和非S[i]!=T[j]因为当您填充LCS[i,j]时,您总是比较S的i-1个字符和T的j-1个字符。

LCS的长度由LCS[M,M]给出

for(i = 0 to M)
{
  LCS[0][i]=0;
}
for(i = 1 to M)
{
  LCS[i][0]=0;
}
理解这一点的最好方法是亲自尝试

在回答第二个问题时,您不需要对算法进行太多修改。

解决方案位于用于检索LCS的表中

为了检索LCS,我们制作了一个额外的表T,其中包含MXM维度的字符。我们修改算法如下

for(i = 1 to M)
{
 for(j = 1 to M)
 {
  if S[i-1] != T[j-1] then
  {
     LCS[i, j] = max(LCS[i - 1, j], LCS[i, j - 1])
     if(LCS[i - 1, j]>=LCS[i, j - 1])          
      T[i-1][j-1]='u'//meaning up
     else T[i-1][j-1]='l'//meaning left
  }
  else
  {
    LCS[i, j] = 1 + LCS[i - 1, j - 1]
    T[i-1][j-1]='d'//meaning diagonally up
  }
 }
}
现在,为了知道(相邻字母的)两个字母共用的最长子串,沿对角线遍历T

for(i = 0 to M)
{
  LCS[0][i]=0;
}
for(i = 1 to M)
{
  LCS[i][0]=0;
}
长度=对角线中连续d的最大数量

任何方形矩阵的对角遍历NXN由完成

包括主对角线的下三角形

j=N-1
while(j>=0)
{
 i=j;k=0;
 while(i <= N-1)
 {
  entry T[i][k];
  ++i;++k
 }
 --j;
}
j=N-1
而(j>=0)
{
i=j;k=0;

而(i表示(1),如果其中一个字符串为空,最长的公共子序列是什么?@dooglius将为0,但不确定何时出现:填写表格…还是在我们调用LCS(i,0)或LCS(0,j)时?在开始填写表格时,可以设置L[0,i]和L[j,0]每一个i,j等于零。这就足够你填写表格的其余部分了。@b外行可以随时查询。