我正在解决 Yandex 问题并遇到了这个问题:
带答案恢复的 NOP 平均值给定两个序列,需要找到并输出它们的最大公共子序列。
让我们提醒您:
如果 x x 是通过从 𝑦 y 中删除几个(可能是零个或全部)元素得到的,则序列 x x 称为序列 y y 的子序列。
最长公共子序列是两个序列的子序列中的最长序列。
输入格式 输入数据的第一行包含数字 N – 第一个序列的长度(1 ≤ N ≤ ≤ 1000)。第二行包含第一个序列的成员(以空格分隔)——绝对值不超过 10,000 的整数。
第三行包含数字 M – 第二个序列的长度(1 ≤ ≤ M ≤ ≤ 1000)。第四行指定第二个序列的成员(以空格分隔)——绝对值不超过 10,000 的整数。
输出格式 要求输出给定序列的最大公共子序列,各个序列之间用空格分隔。如果有多个这样的子序列,那么输出其中任何一个都可以。
注意:在示例2中,有三个最大公共子序列。
1
2
3
其中任何一个都是正确答案。
首先我写了一个常规解决方案,它从第一个序列中取出所有子序列(从最长的开始),并将它们与第二个序列中相同长度的子序列进行比较。前几次测试都成功了,但后来我阅读了条件并意识到困难在于可以从序列中任意删除元素。
也就是说,如果给出两个序列:
1 2 3 4 5
1 7 4 3 5
那么答案就是1 4 5
我不知道如何解决这个问题。

有这样一个算法的思路(LCS via DP)让我们有两个序列:
A[0...N-1]B[0...M-1]创建一个二维数组
dp[N+1][M+1],其中:dp[i][j]A[0:i]—和之间的最长公共子序列的长度B[0:j]。公式如下:
如果我们讨论完整的代码,我可以举一个例子
Python设s和t为要寻找最长公共子序列的字符串:s由符号s i组成,其中1 ≤ i ≤ m,m是字符串s的长度。t由字符t j组成,其中1 ≤ j ≤ n,n是字符串t的长度。
符号s [1,i]表示长度为i的字符串s的前缀。类似地,引入t [1,j] ——前缀t。
s [1,0]和t [1,0]是空字符串。
给定两个前缀s [1,i]和t [1,j]。我们感兴趣的是最长公共子序列的长度,我们用f i,j表示。
f的属性是什么?
f i,0 = f 0,j = 0 – 任何字符串与空字符串没有任何共同之处,因此为零。
f i+1,j ≥ f i,j , f i,j+1 ≥ f i,j – 单调性。前缀越长,公共部分越长(而不是更短)。不能通过添加新符号来缩短解决方案。
f i-1,j-1 ≥ f i,j - 1 – f不会增长得太快。如果两个前缀都缩短一个字符,那么只能从整个序列中删除最后一个字符,不能再删除其他字符。
若s i = t j则f i,j = f i-1,j-1 + 1。如果符号相等,则向最长子序列 ( f i-1,j-1 )加 1 (等于s i和t j 的符号)。由于属性 3,无法添加两个或两个以上。
如果s i ≠ t j则f i,j = f i-1,j或f i,j = f i,j-1。如果符号不相等,则最长序列不能同时以s i和t j结束。
如果它不以s i结尾,则f i,j = f i-1,j,因为可以删除符号s i而不会影响解的长度。
如果它没有在t j处结束,那么f i,j = f i,j-1,因为可以删除符号t j而不会影响解的长度。
将属性 1、4、5 放在一起,我们得到公式:
如果i = 0或j = 0,则f i, j = 0(属性 1);
f i,j = f i-1,j-1 + 1,如果i,j > 0且s i = t j (性质 4);
f i,j = max(f i-1,j , f i,j-1 ),如果i, j > 0且s i ≠ t j(属性 5)。
如果已知f i-1,j、f i,j-1、f i-1,j-1 的值,该公式使我们能够计算f i,j。计算f i,j的时间和内存复杂度都是 O(ij)。如果只需要计算长度,内存复杂度可以降低到O(min(i, j)) 。如果除了长度之外,我们还需要呈现一般序列,那么我们将必须存储f 的所有值。
子序列从尾到头进行重建。
我们需要从位置(i, j)移动到位置
如果i = 0或j = 0,则搜索结束。每当路线上出现等式s i = t j时,相应的符号就会写入答案。最后,答案必须反过来。或者,如果您想避免逆转,请从字符串s和t的末尾开始构建子序列。