动态规划背后的想法是逆向评估递归函数,从基本案例开始,迭代地构建越来越大的子问题的答案,直到计算出整体的答案输入问题。
如果你递归地评估这个函数,那么你绝对会在你提到的情况下通过递减 i 和 j 来递归。但是,在动态编程版本中,您不是从 LCS[i, j] 开始并尝试通过评估 LCS[i-1, j-1] 来评估它。您从 LCS[i-1, j-1] 开始并使用它来评估 LCS[i, j]。
具体来说,这段代码首先通过直接使用基本案例解决方案为所有 i 和 j 计算 LCS[i, 0] 和 LCS[0, j]。接下来,它使用所有 j 都知道 LCS[0, j] 的事实来计算所有 j 的 LCS[1, j]。然后它使用 LCS[1, j] 对所有 j 都已知这一事实来计算所有 j 的 LCS[2, j],依此类推。
因此,是时候计算 LCS[i, j] 并且您的特定情况适用,该算法不需要递减 i 或 j 并递归地向下递减。它已经计算出 LCS[i-1, j-1],因此它可以读取该值并继续构建表中的其余值。
这可能是最容易直观地看到的。假设您要查找字符串“canon”和“annie”的 LCS。我们从这个 2D 表格开始:
A N N I E
. . . . . .
C . . . . . .
A . . . . . .
N . . . . . .
O . . . . . .
N . . . . . .
最初,我们为所有 i 和 j 设置 LCS[0, j] = LCS[i, 0] = 0:
A N N I E
0 0 0 0 0 0
C 0 . . . . .
A 0 . . . . .
N 0 . . . . .
O 0 . . . . .
N 0 . . . . .
现在,我们将逐行浏览此表,并使用您在原始问题中描述的重复顺序填写缺失的条目。遍历第一行时,我们将比较字母 C 和单词“ANNIE”中的所有字母。我们永远找不到匹配项,所以我们总是使用递归 LCS[i, j] = max(LCS[i - 1, j] + LCS[i, j - 1])。这总是计算为零,所以我们得到:
A N N I E
0 0 0 0 0 0
C 0 0 0 0 0 0
A 0 . . . . .
N 0 . . . . .
O 0 . . . . .
N 0 . . . . .
这是有道理的,因为该表的第一行表示字符串 C 的 LCS 的长度以及 ANNIE 的所有前缀。
在下一行中,我们将尝试查找字符串 CA 的 LCS 和 ANNIE 的所有后缀。我们考虑的第一个条目匹配 A 和 A。由于这是一个匹配,我们使用递归 LCS[i, j] = 1 + LCS[i - 1, j - 1],其计算结果为 1:
A N N I E
0 0 0 0 0 0
C 0 0 0 0 0 0
A 0 1 . . . .
N 0 . . . . .
O 0 . . . . .
N 0 . . . . .
同样,我们可以通过注意“CA”和“A”的 LCS 是长度为 1 的序列 A 来检查这一点。
这里的重要细节是我们不会在此处减少 i 或 j。我们仍然需要填写此行的其余部分,因此我们将继续前进。
对于这一行的其余条目,我们将 A 与 ANNIE 的每个字符进行比较,发现它不匹配。因此,我们将使用递归 LCS[i, j] = max(LCS[i-1, j], LCS[i, j-1]),它总是通过从其余部分中提取 1 来评估为 1行的。此处显示:
A N N I E
0 0 0 0 0 0
C 0 0 0 0 0 0
A 0 1 1 1 1 1
N 0 . . . . .
O 0 . . . . .
N 0 . . . . .
继续下一行为我们提供了以下信息:
A N N I E
0 0 0 0 0 0
C 0 0 0 0 0 0
A 0 1 1 1 1 1
N 0 1 2 2 2 2
O 0 . . . . .
N 0 . . . . .
同样,这是有道理的。 “CAN”和“A”的LCS就是“A”,“CAN”和“AN”的LCS就是“AN”,等等
我们可以通过表格的其余部分重复此操作以找到此结果表格:
A N N I E
0 0 0 0 0 0
C 0 0 0 0 0 0
A 0 1 1 1 1 1
N 0 1 2 2 2 2
O 0 1 2 2 2 2
N 0 1 2 3 3 3
我们知道 LCS 的长度为 3,这是正确的。
希望这会有所帮助!