【问题标题】:How do I memoize this program?我如何记住这个程序?
【发布时间】:2021-11-16 07:37:12
【问题描述】:

PepCoding | Count Binary Strings.

  1. 给你一个数字 n。
  2. 您需要打印长度为 n 且不包含连续 0 的二进制字符串的数量。

我的代码:

void solve(int n ,string ans  , vector<string> &myAns){
  if(ans.size()==n) {
    //cout << ans << "\n";
    myAns.push_back(ans);
    return;
  }
  if(ans.back() == '0'){
    solve(n , ans + "1" , myAns);
  }
  else{
    solve(n , ans + "0" , myAns);
    solve(n , ans + "1" , myAns);
  }
}

我应该如何将重复出现的字符串存储在哈希或其他东西中?

【问题讨论】:

  • Edit 你的问题包括对你的问题的描述。问题应该是独立的,不依赖外部链接来保证完整性。

标签: c++ algorithm dynamic-programming


【解决方案1】:

请注意,您需要打印 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" 前面构成的所有字符串.任何其他组合都会导致连续的零。

【讨论】:

    【解决方案2】:

    首先,此算法不需要动态编程或递归。您可以简单地使用下面的(请注意,这是第一个草图,可能存在更有效的解决方案):

      size_t currentLength = 1;
      results_.push_back("0");
      results_.push_back("1");
    
      while(currentLength < length_)
      {
        size_t size = results_.size();  
        for(size_t i=0; i < size; i++)
        {
          string& elem = results_[i];
          if(elem.back() == '0')
          {
              elem.push_back('1');
          }
          else
          {
              results_.push_back(elem + "0");
              elem.push_back('1');
          }
        }
        currentLength = results_.back().size();
      }
    

    如果你仍然想记忆,你需要创建一个缓存,它是最简单形式的 std::map 或 std::unordered_map。通常你可以做的是从输入中创建一个唯一的键并保存返回值。

    【讨论】:

    • 当我还是个孩子的时候,我们甚至没有动态编程。我们只是编程。我们喜欢这样。我们在 20 英尺的积雪中双向编码了 40 英里的上坡路。
    • 我们所有的编程都是静态的,我们喜欢这样:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-11
    • 1970-01-01
    • 1970-01-01
    • 2015-12-12
    • 2017-02-17
    • 2016-12-13
    相关资源
    最近更新 更多