📣 VIP通行证夏日特惠 限时立减$68
楼主: yyoung
跳转到指定楼层
上一主题 下一主题
收起左侧

[树/链表/图] 在leetcode面经上发现一道有意思的题目,大家可以一起讨论一下

🔗
Wwwyyy 2019-6-16 09:19:23 | 只看该作者
全局:
rikoizz 发表于 2019-6-16 08:43
cf 3500 的大佬都没做出来....我觉得作为面试题八成是缺少某种约束条件

感觉这个题不像candy crush,更像祖玛啊哈哈。蛤蟆来决定哪段先消除。面试官少给一个参数frog。txtx
回复

使用道具 举报

🔗
337845818 2019-6-16 10:07:22 | 只看该作者
全局:
这题真的挺有意思的哈哈
回复

使用道具 举报

🔗
qqqzhouhk 2019-6-16 11:04:34 | 只看该作者
全局:
感觉和488有点像,期待大神O(n)的解法!
回复

使用道具 举报

🔗
magicsets 2019-6-16 14:40:39 | 只看该作者
全局:
magicsets 发表于 2019-6-16 02:56
这种情况之前确实没有考虑.. 要解决的话可以再加一个状态函数:

给定输入数组S,定义 H(i, j) 取值如 ...

之前描述状态转移方程的时候有点混乱..(但是关于 C(i, j) 和 H(i, j) 的定义应该还是比较明确的)

写了一份代码这样比较好参照:
  1. #include <algorithm>
  2. #include <iostream>
  3. #include <vector>

  4. constexpr static int kNotSet = -1;
  5. constexpr static int kInvalid = -2;
  6. class MaxRepetition;

  7. // Crushable(i, j) = true 当且仅当 data[i..j] 经过任意变换后可以被完全消除
  8. class Crushable {
  9. public:
  10.   explicit Crushable(const size_t length)
  11.       : states_(length, std::vector<int>(length, kNotSet)) {}

  12.   bool Get(int i, int j) {
  13.     if (states_[i][j] == kNotSet) {
  14.       states_[i][j] = Evaluate(i, j);
  15.     }
  16.     return static_cast<bool>(states_[i][j]);
  17.   }

  18.   void SetMaxRepetition(MaxRepetition* max_repetition) {
  19.     max_repetition_ = max_repetition;
  20.   }

  21. private:
  22.   bool Evaluate(int i, int j);

  23.   MaxRepetition* max_repetition_;
  24.   std::vector<std::vector<int>> states_;
  25. };

  26. // (1) 如果 data[i] != data[j],那么 MaxRepetition(i, j) = kInvliad
  27. // (2) 否则不妨设 data[i] == data[j] == X,那么 MaxRepetition(i, j)
  28. //     取值为 data[i..j] 经过任意消除过程后可以得到的最长的"全X" 串的长度;
  29. //     如果不能消除到只剩下X,那么 MaxRepetition(i, j) 取值仍然为kInvalid
  30. class MaxRepetition {
  31. public:
  32.   MaxRepetition(const std::vector<int>& data)
  33.       : data_(data),
  34.         states_(data.size(), std::vector<int>(data.size(), kNotSet)) {}

  35.   int Get(int i, int j) {
  36.     if (states_[i][j] == kNotSet) {
  37.       states_[i][j] = Evaluate(i, j);
  38.     }
  39.     return states_[i][j];
  40.   }

  41.   void SetCrushable(Crushable* crushable) {
  42.     crushable_ = crushable;
  43.   }

  44. private:
  45.   int Evaluate(int i, int j);

  46.   const std::vector<int> &data_;
  47.   Crushable* crushable_;
  48.   std::vector<std::vector<int>> states_;
  49. };

  50. ////////////////////////////////////////////////////////////////////////////////
  51. /////////////////// Crushable 与 MaxRepetition 的状态转移方程 /////////////////////
  52. ////////////////////////////////////////////////////////////////////////////////

  53. bool Crushable::Evaluate(int i, int j) {
  54.   // 边界条件:区间长度小于3
  55.   if (i + 1 >= j) {
  56.     return false;
  57.   }
  58.   // 如果存在i < k < j,使得Crushable(i, k)且Crushable(k + 1, j),那么返回true
  59.   for (int k = i + 2; k < j - 2; ++k) {
  60.     if (Get(i, k) && Get(k + 1, j)) {
  61.       return true;
  62.     }
  63.   }
  64.   // 否则考虑是否可以Crush中间若干区间,然后剩下来的同一字符数量大于等于3
  65.   return max_repetition_->Get(i, j) >= 3;
  66. }

  67. int MaxRepetition::Evaluate(int i, int j) {
  68.   // 边界条件
  69.   if (data_[i] != data_[j]) {
  70.     return kInvalid;
  71.   }
  72.   // 区间大小为1或者2的情况
  73.   if (i + 1 >= j) {
  74.     return j - i + 1;
  75.   }
  76.   int max_count = kInvalid;
  77.   // Case 1
  78.   if (crushable_->Get(i + 1, j - 1)) {
  79.     max_count = 2;
  80.   }
  81.   // Case 2
  82.   const int target_value = data_[i];
  83.   for (int k = i + 1; k < j; ++k) {
  84.     if (data_[k] != target_value) {
  85.       continue;
  86.     }
  87.     const int left_count = Get(i, k);
  88.     const int right_count = Get(k, j);
  89.     if (left_count != kInvalid && right_count != kInvalid) {
  90.       max_count = std::max(max_count, left_count + right_count - 1);
  91.     }
  92.   }
  93.   return max_count;
  94. }

  95. std::vector<int> ComputeShortestRemnant(const std::vector<int>& data) {
  96.   const int length = data.size();
  97.   if (length == 0) {
  98.     return {};
  99.   }

  100.   Crushable crushable(length);
  101.   MaxRepetition max_repetition(data);
  102.   crushable.SetMaxRepetition(&max_repetition);
  103.   max_repetition.SetCrushable(&crushable);

  104.   // 外层的动态规划:找到总长度最大的Crushable区间集合
  105.   std::vector<int> max_crush(length);
  106.   std::vector<int> backtrack(length);
  107.   backtrack[0] = -1;
  108.   for (int j = 2; j < length; ++j) {
  109.     max_crush[j] = max_crush[j - 1];
  110.     backtrack[j] = j - 1;
  111.     for (int i = 0; i < j - 1; ++i) {
  112.       if (crushable.Get(i, j)) {
  113.         const int total_crush = (i == 0 ? 0 : max_crush[i - 1]) + j - i + 1;
  114.         if (total_crush > max_crush[j]) {
  115.           max_crush[j] = total_crush;
  116.           backtrack[j] = i - 1;
  117.         }
  118.       }
  119.     }
  120.   }

  121.   // Backtrack收集无法消除的元素
  122.   std::vector<int> result;
  123.   int curr = length - 1;
  124.   while (curr != -1) {
  125.     const int prev = backtrack[curr];
  126.     if (prev == curr - 1) {
  127.       result.push_back(data[curr]);
  128.     }
  129.     curr = prev;
  130.   }

  131.   std::reverse(result.begin(), result.end());
  132.   return result;
  133. }

  134. void PrintVector(const std::vector<int>& values) {
  135.   std::cout << "[";
  136.   bool first = true;
  137.   for (int value : values) {
  138.     std::cout << (first ? "" : " ") << value;
  139.     first = false;
  140.   }
  141.   std::cout << "]\n";
  142. }

  143. int main(int argc, char* argv[]) {
  144.   // [1 1]
  145.   PrintVector(ComputeShortestRemnant({1,3,3,3,2,2,2,3,1}));

  146.   // [1 1]
  147.   PrintVector(ComputeShortestRemnant({1,3,3,3,2,2,2,3,1}));

  148.   // [3 1 3 2 3]
  149.   PrintVector(ComputeShortestRemnant(
  150.       {3,1,2,2,2,1,1,1,2,2,3,1,1,2,2,2,1,1,1,2,3}));

  151.   // [2]
  152.   PrintVector(ComputeShortestRemnant({2,3,3,3,2,2,2,3,3}));

  153.   // []
  154.   PrintVector(ComputeShortestRemnant({5,1,1,1,5,1,1,1,5}));

  155.   // []
  156.   PrintVector(ComputeShortestRemnant({1,1,1,2,2,2,1,2,2,2,1}));

  157.   // []
  158.   PrintVector(ComputeShortestRemnant(
  159.       {3,1,1,1,2,1,1,1,2,1,1,1,2,1,1,1,2,2,1,1,
  160.        1,3,1,1,1,3,2,2,2,3,1,1,1,3,1,1,1,3}));

  161.   return 0;
  162. }
复制代码

补充内容 (2019-6-16 14:47):
Crushable对应原帖描述的C(i, j),MaxRepetition对应原帖描述的H(i, j),都是用了memo的方法,总时间复杂度为O(n ^ 3)
回复

使用道具 举报

🔗
loonreg 2019-6-17 02:57:18 | 只看该作者
全局:
magicsets 发表于 2019-6-15 12:40
这个问题可以用两阶段的动态规划做吧,时间是O(n^3),不知道可不可以进一步优化了

--

1. C(i, j)的计算可以参考POJ 1390的题解,用dfs + dp 求解。对于这道题,rlen的长度可以限制在0到3之间。时间复杂度O(n^3)
2. 不需要排序,C(i, j) 是个0/1矩阵,这个矩阵本身决定了区间的顺序。后面对区间做dp时,需要对右端点排序,但是在计算dp[j] 的时候,只需要枚举所有的 C(i, j) 即可。
3. 这题看起来似乎无论如何都无法避免计算C(i, j)。想要有更好的方法,不妨直接考虑如何求解所有的C(i, j)。因为剩下的部分可以在O(n^2)时间内解决
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表