使用递归的最长公共子序列

Longest common subsequence using recursion

本文关键字:递归      更新时间:2024-09-29

基本上,我试图使用递归来解决最长的公共子序列问题。在其中一个网站上,我应该写下以下形式的函数;

int longestCommonSubstr (string S1, string S2, int n, int m)
{
// your code here
}

当给出附加参数nm时,我可以编写它的递归代码。其中n and m分别是串S1、S2的长度。另一个网站要求我解决同样的问题,但没有提供nm,如下所示;

int longestCommonSubstr (string S1, string S2)
{
// your code here
}

对于第一个站点,我的完整递归函数如下所示;

int longestCommonSubstr (string S1, string S2, int n, int m)
{
// your code here
if(n==0 || m==0){
return 0;
}
if(S1[n-1]==S2[m-1]){
return 1+longestCommonSubstr(S1,S2,n-1,m-1);
}
else{
return max(longestCommonSubstr(S1,S2,n-1,m),longestCommonSubstr(S1,S2,n,m-1));
}
}

但当我们不允许使用参数nm作为第二种情况时,我无法编写递归代码很乐意寻求帮助来处理第二种情况的递归代码。

如果需要,您完全可以使用递归解决方案。毕竟,函数可以调用其他函数。只需计算它们并将它们传递给递归函数即可。

但这只是部分帮助。即使是极客对极客(顺便说一句,这是一个糟糕的网站(,时间复杂性要求也是O(n*m(,递归无法满足这一要求。

这不仅仅是一个递归练习,也是一个动态编程练习。递归解决方案通常被认为是";暴力";解决方案,因为它通常必须重复解决相同的子问题。

您可以通过记忆已经遇到的子解决方案(也称为自上而下的方法(来帮助自己,但每次迭代仍要进行大约三次递归调用。

您需要更进一步,并派生出一个自下而上的解决方案。

我们将创建一个2D阵列。行是S1的字符,列是S2的行。我们将在开头添加一行和一列,以表示其他字符串为空。

因此,给定:S1 = "ABCDGH";S2 = "ACDGHR;",我们从一个数组开始,我将称之为counts:

0 A C D G H R
0 0 0 0 0 0 0 0
A 0
B 0
C 0
D 0
G 0
H 0

然后我们可以进行直接的人物间比较。如果是S1[i] == S2[j],我们将1添加到counts[i - 1][j - 1]的值中。这就是我们跟踪子字符串长度的方式。我们将保留最大长度的值,只要找到两个匹配的字符,我们就会根据需要更新最大长度。

如果是S1[i] != S2[j],我们只需将0存储在counts[i][j]中。当我们填满整个2D阵列时,我们知道了最大值。不需要递归。

以下是填充数组的样子:

0 A C D G H R
0 0 0 0 0 0 0 0
A 0 1 0 0 0 0 0
B 0 0 0 0 0 0 0
C 0 0 1 0 0 0 0
D 0 0 0 2 0 0 0
G 0 0 0 0 3 0 0
H 0 0 0 0 0 4 0

这是代码:

class Solution {
public:
std::vector<std::vector<int>> substrCounts(S1.length() + 1,
std::vector<int>(S2.length() + 1));
int maxCount = 0;
for (std::size_t rowIdx = 1; rowIdx < substrCounts.size(); ++rowIdx) {
for (std::size_t columnIdx = 1; columnIdx < substrCounts[0].size();
++columnIdx) {
if (S1[rowIdx - 1] == S2[columnIdx - 1]) {
substrCounts[rowIdx][columnIdx] =
substrCounts[rowIdx - 1][columnIdx - 1] + 1;
maxCount = std::max(maxCount, substrCounts[rowIdx][columnIdx]);
} else {
substrCounts[rowIdx][columnIdx] = 0;
}
}
}
return maxCount;  
}
};

它通过了所有119个关于极客的极客测试用例,他们以0.05秒计算运行时间。您还会注意到,我根本不关心nm;它们是不需要的。

我通常只会更改签名,但网站不允许。然后我应该将未使用的参数强制转换为void,但网站运行时根本没有任何警告,这让你可以编写你能想象到的最糟糕的代码。我没有为演员阵容而烦恼,只是因为我觉得自己很懒,当你被迫使用#include <bits/stdc++.h>using namespace std;时,喋喋不休地谈论最佳实践是毫无意义的。