请注意,您需要打印 number 个长度为 n 且没有连续 0 的二进制字符串,而不是实际形成(并存储或打印)所有这些字符串。
所以,是的,递归解决方案可以极大地受益于记忆,但不是所有的单个字符串。您应该存储的是每个长度的 number 个字符串。 两个数字,实际上是一个以1 开头的字符串和一个以0 开头的字符串。
long long solve(long long n, bool is_previous_a_zero = false)
{
// Memoize the number of strings for each length and
// possible previous character.
using key_t = std::pair<long long, bool>;
static std::map<key_t, long long> mem{
{{1, false}, 2}, // "0", "1"
{{1, true}, 1} // "1" after a "****0"
};
if ( n < 1 ) {
return 0;
}
// First check if the result is already memoized.
key_t key{ n, is_previous_a_zero };
auto const candidate = mem.lower_bound(key);
if( candidate != mem.cend() and candidate->first == key ) {
return candidate->second;
}
// Otherwise evaluate it recursively and store it, before returning.
long long result = is_previous_a_zero
? solve(n - 1, false)
: solve(n - 1, true) + solve(n - 1, false);
mem.emplace_hint(candidate, key, result);
return result;
}
Here 这种方法已针对最长 45 的所有长度进行了测试。
请注意,如果您只对运行时效率感兴趣,您可以在编译时预先计算所有这些数字。
template< std::size_t N >
constexpr auto make_lut()
{
std::array<long long, N + 1> lut{0, 2, 3};
for ( size_t i = 3; i <= N; ++i) {
lut[i] = lut[i - 1] + lut[i - 2]; // Yeah, it's a Fibonacci sequence.
}
return lut;
}
// Then it can be called like so
constexpr auto lut{ make_lut<45>() };
// The number of binary strings of length n with no consecutive 0's = lut[n];
你可以测试一下here。
为了证明最后的 sn-p 确实解决了贴出的问题,请考虑以下几点:
-
只有 2 个可能的长度为 1 的二进制字符串:"0" 和 "1"。
-
长度为 2 且不包含连续 0 的 3 个可能的二进制字符串:"01"、"10" 和 "11"。
-
所有可能的长度为n 的字符串都是在所有长度为n - 1 的字符串前面加上"1" 组成的所有字符串,以及在所有长度为n - 2 的字符串前面加上"01" 前面构成的所有字符串.任何其他组合都会导致连续的零。