Nuro ai interview preparation

2019-09-04
  1. 点云聚类,使用union find,按轴排序,也可以使用KD tree进行搜索(https://en.wikipedia.org/wiki/K-d_tree)
  2. alien dictionary/string multiplication(multiply two numbers. number 是 string)
  3. conv2D,自定义kernel,如果想根据内容删除8领域联通的一行,又希望删掉的这一行重要性最小怎么做。(简单的DP) 问了一些基础的 性能优化的问题。
  4. 已知各个数字的概率,求get_num()获取随机数字。
  5. lc 1235 profit为1的情况。
  6. closest neighbors in a map,怎么存,grid/quadtree的比较,怎么sharding,grid id还是partition id的比较
  7. sparse vector的加和乘,自己实现数据结构。之后问了问DL的知识什么是overfitting,underfitting。
  8. 1 producer 1 consumer concurrent queue,要求自己写 data structure,没要求我加 condition variable,我就写了一个很基本的带锁的 linked list,写完之后分析了不 上锁会出现什么情况。答完之后还讨论了怎么无锁实现,面试官告诉我可以用一 个 dummy head。
  9. 先问了很基本的转置矩阵然后是加上 concurrency,假设给 k 个 processor,问应 该怎样给第 i 个 processor 分配
  10. 多线程安全 queue。 C++ rvalue 相关. C++ move: std::move does nothing but cast its argument to an rvalue。https://www.zhihu.com/question/277908001
template<typename T>
T&& move(T& v) { 
 return static_cast<T&&>(v); 
}
  1. 实现一个数据结构,keep 最新的 k 个 streaming data,拿到平均数和众数。拿平均数和 众数的复杂度要求均为 O(1)。

    // All O(1) data structure
    class AllOne {
     private:
      struct Node {
        int count = 0;
        unordered_set<string> keys;
        Node(const int cnt, const string& key) {
          count = cnt;
          keys.insert(key);
        }
      };
      list<Node> data_;
      unordered_map<string, list<Node>::iterator> record_;
         
     public:  
      /** Initialize your data structure here. */
      AllOne() {}
           
      /** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
      void inc(string key) {
        if (record_.find(key) == record_.end()) {
          auto it = data_.insert(data_.begin(), Node(0, key));
          record_[key] = it;
        }
           
        auto next = record_[key];
        auto cur = next++;
        if (next == data_.end() || next->count > cur->count + 1) {
          next = data_.insert(next, Node(cur->count+1, key));
        }
        next->keys.insert(key);
        cur->keys.erase(key);
        if (cur->keys.empty()) {
          data_.erase(cur);
        }
        record_[key] = next;
      }
           
      /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
      void dec(string key) {
        if (record_.find(key) == record_.end())
          return;
           
        auto prev = record_[key];
        auto cur = prev--;
        if (cur == data_.begin() || prev->count + 1 < cur->count) {
          prev = data_.insert(cur, Node(cur->count-1, key));
        }
        prev->keys.insert(key);
        cur->keys.erase(key);
        if (cur->keys.empty()) {
          data_.erase(cur);
          record_.erase(key);
        }
        if (prev->count <= 0) {
          data_.erase(prev);
          record_.erase(key);
        } else {
          record_[key] = prev;
        }
      }
           
      /** Returns one of the keys with maximal value. */
      string getMaxKey() {
        return data_.size() > 0? *(data_.back().keys.begin()) :  "";
      }
           
      /** Returns one of the keys with Minimal value. */
      string getMinKey() {
         return data_.size() > 0? *(data_.front().keys.begin()) :  "";
      }
    };
    
  2. 大概是一堆堆集的不同颜色的方块,你通过左右互换的方式交换他们的位置。然后如果相邻 (上下左右)的相同颜色的方块超过或者等于 3 个,你就需要消掉他们。问 5 步以内是否 可以完成?如果能,输出解,不能的话,输出-1。可以看下图理解一下。

  3. 求两数组相差多少次shift A = [1,3,4,5,2] B = [4,5,2,1,3] 比如这里A两次shift可得到B array中所有数值不同 直接要求time complexity < O(N), N = len(A); 给出的解法是 O(sqrt(N)) 取 M = sqrt(N) hashmap A[:M], value to index 遍历查看 B[0], B[M], B[2M], …. 是否在hashmap中

  4. 实现一个SparseMatrix的类 支持 set, get, transpose, add, multiply set, get, transpose, add, multiply 类似励筘傘药药 具体看 https://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=657461&highlight=nuro

  5. Given a list of 3-d points [(1,2,5), (5,3,2), …., ] and a distance K If point A and point B are within K distance, group them together. If distance(A, B) <= K and distance (B,C) <= K, group A, B, C together. Return a list of groups.

  6. system programming给了我一段cache的代码。如果value不再cache里就要跑一个time consuming function。然后要我implement concurrency using locks and cv。比如两个thread同时access cache应该怎么处理.

  7. 数据存储压缩,需要对cpp STL和bit manipultion有深刻的理解才行。bit_set class。count, size, test, any, none, all.

  8. RANSAC: https://zhuanlan.zhihu.com/p/62238520

使用模型尽可能多match点。


// 311. Sparse Matrix Multiplication
vector<vector<int>> multiply(vector<vector<int>>& mat1, vector<vector<int>>& mat2) {
  unordered_map<int, unordered_set<int>> sparse_index1;
  unordered_map<int, unordered_set<int>> sparse_index2;
  
  const int k = mat1.size();
  const int n = mat2[0].size();
  for (int i = 0; i < mat1.size(); ++i) {
    for (int j = 0; j < mat1[0].size(); ++j) {
      if (mat1[i][j]) {
        sparse_index1[i].insert(j);
      }
    }
  } 
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < mat2.size(); ++j) {
      if (mat2[j][i]) {
        sparse_index2[i].insert(j);
      }
    }
  }
  vector<vector<int>> ans(k, vector<int>(n, 0));
  
  for (int i = 0; i < k; ++i) {
    for (int j = 0; j < n; ++j) {
      if (!sparse_index1[i].empty() && !sparse_index2[j].empty()) {
        const auto& j_col = sparse_index2[j];
        for (const auto& ele : sparse_index1[i]) {
          if (i == 1) cout << ele << " " << i << " " << j << endl;
          if (j_col.count(ele) > 0) {
            ans[i][j] += mat1[i][ele]*mat2[ele][j];
          }
        }
      }
    }
  }
  
  return ans;
}
// 1570. Dot Product of Two Sparse Vectors
class SparseVector {
 private:  
  unordered_map<int, int> record_; 
  
 public:
  SparseVector(vector<int> &nums) {
    for (int i = 0; i < nums.size(); ++i) {
      if (nums[i]) {
        record_[i] = nums[i];
      }
    }     
  }
    
  // Return the dotProduct of two sparse vectors
  int dotProduct(SparseVector& vec) {
    int res = 0;
    const auto& num1 = vec.record_.size() >= record_.size()? record_ : vec.record_;
    auto& num2 = vec.record_.size() >= record_.size()? vec.record_ : record_;
    for (const auto& [idx, val] : num1) {
      res += num2[idx] *val;
    }
    return res;
  }
};
// 239. Sliding Window Maximum
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
  deque<int> cur_max;
  vector<int> max_arrs;
  for (int i = 0; i < nums.size(); ++i) {
    while (!cur_max.empty() && nums[i] >= nums[cur_max.front()]) {
      cur_max.pop_front();
    }
    cur_max.push_front(i);
    while (cur_max.back() + k - 1 < i) cur_max.pop_back();
    if (i >= k - 1) {
      max_arrs.emplace_back(nums[cur_max.back()]);
    }
  }
  
  return max_arrs;
}

// 480. Sliding window median
// Insert to left, then transfer left largest to right, compare size for transfering.
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
  multiset<int> left;
  multiset<int> right;
  vector<double> ans;
  
  for (int i = 0; i < nums.size(); ++i) {
    if (i >= k) {
      if (nums[i - k] <= *left.rbegin()) 
        left.erase(left.find(nums[i-k]));
      else
        right.erase(right.find(nums[i-k]));
    }
    left.insert(nums[i]);
    right.insert(*left.rbegin());
    left.erase(prev(left.end()));
    
    if (left.size() < right.size()) {
      left.insert(*right.begin());
      right.erase(right.begin());
    }
    if (i >= k-1) {
      ans.emplace_back(k&1?*left.rbegin():*left.rbegin() * 0.5 + *right.begin()*0.5);
    }
  }
  
  
  return ans;
}

// Sliding window mode is the LFU cache, think about this.
// https://stackoverflow.com/questions/43825229/maximum-absolute-difference-in-array-within-time-window
// 432. All O`one Data Structure
// Method 1: list
class AllOne {
 private:
  struct Node {
    int count = 0;
    unordered_set<string> keys;
    Node(const int cnt, const string& key) {
      count = cnt;
      keys.insert(key);
    }
  };
  
  list<Node> data_;
  unordered_map<string, list<Node>::iterator> record_;
  
 public:  
  /** Initialize your data structure here. */
  AllOne() {}
    
  /** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
  void inc(string key) {
    if (record_.find(key) == record_.end()) {
      auto it = data_.insert(data_.begin(), Node(0, key));
      record_[key] = it;
    }
    
    auto next = record_[key];
    auto cur = next++;
    if (next == data_.end() || next->count > cur->count + 1) {
      next = data_.insert(next, Node(cur->count+1, key));
    }
    next->keys.insert(key);
    cur->keys.erase(key);
    if (cur->keys.empty()) {
      data_.erase(cur);
    }
    record_[key] = next;
  }
    
  /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
  void dec(string key) {
    if (record_.find(key) == record_.end())
      return;
    
    auto prev = record_[key];
    auto cur = prev--;
    if (cur == data_.begin() || prev->count + 1 < cur->count) {
      prev = data_.insert(cur, Node(cur->count-1, key));
    }
    prev->keys.insert(key);
    cur->keys.erase(key);
    if (cur->keys.empty()) {
      data_.erase(cur);
      record_.erase(key);
    }
    if (prev->count <= 0) {
      data_.erase(prev);
      record_.erase(key);
    } else {
      record_[key] = prev;
    }
  }
    
  /** Returns one of the keys with maximal value. */
  string getMaxKey() {
    return data_.size() > 0? *(data_.back().keys.begin()) :  "";
  }
    
  /** Returns one of the keys with Minimal value. */
  string getMinKey() {
     return data_.size() > 0? *(data_.front().keys.begin()) :  "";
  }
};

// Method 2: map
class AllOne {
 public:
  map<int, unordered_set<string>> record_;
  unordered_map<string, int> data_;
    
  /** Initialize your data structure here. */
  AllOne() {}
    
  /** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
  void inc(string key) {
    if (data_.find(key) != data_.end()) {
      record_[data_[key]].erase(key);
      if (!record_[data_[key]].size())
        record_.erase(data_[key]);
    }
    ++data_[key];
    record_[data_[key]].insert(key);
  }
    
  /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
  void dec(string key) {
    if (data_.find(key) == data_.end())
      return;
    const int cnt = data_[key];
    --data_[key];
    if (!data_[key]) {
      data_.erase(key);
    }
    record_[cnt].erase(key);
    if (!record_[cnt].size()) {
      record_.erase(cnt);
    }
    if (data_.find(key) != data_.end())
      record_[cnt-1].insert(key);
  }
    
  /** Returns one of the keys with maximal value. */
  string getMaxKey() {
    if (!record_.size()) return "";
    return *(record_.crbegin()->second.begin());    
  }
    
  /** Returns one of the keys with Minimal value. */
  string getMinKey() {
    if (!record_.size()) return "";
    return *(record_.cbegin()->second.begin());        
  }
};
// LFU & LRU cache
// 146. LRU Cache
class LRUCache {
 public:
  unordered_map<int, pair<int, list<int>::iterator>> record_;
  list<int> key_;
  int capacity_ = 0;
  
  LRUCache(int capacity) {
    capacity_ = capacity;
  }
    
  int get(int key) {
    auto it = record_.find(key);
    if (it == record_.end()) return -1;
    const auto value = it->second;
    record_.erase(it);
    key_.erase(value.second);
    key_.push_front(key);
    record_[key] = make_pair(value.first, key_.begin());
    return value.first;
  }
    
  void put(int key, int value) {
    auto it = record_.find(key);
    if (it != record_.end()) {
      const auto [_, it_list] = it->second;
      record_.erase(it);
      key_.erase(it_list);
    }
    if (key_.size() >= capacity_) {
      auto back_it = prev(key_.end());
      record_.erase(*back_it);
      key_.erase(back_it);
    }
    key_.push_front(key);
    record_[key] = std::make_pair(value, key_.begin());
  }
};

// 460. LFU Cache
// Method 1
class LFUCache {
 private:
  struct Node {
    int count_ = 0;
    int value_ = 0;
    int key_ = 0;
    Node(const int cnt, const int v, const int k):
     count_(cnt), value_(v), key_(k) {}
  };
  
  unordered_map<int, list<Node>> data_;
  unordered_map<int, list<Node>::iterator> keys_;
  int min_count_ = INT_MAX;
  int capacity_ = 0;
  
 public:  
  LFUCache(int capacity) {
    capacity_ = capacity;
  }
    
  int get(int key) {
    if (keys_.find(key) == keys_.end())
      return -1;
    const auto it = keys_[key];
    const int prev_count = it->count_;
    const int value = it->value_;
    
    data_[prev_count].erase(it);
    if (data_[prev_count].empty()) {
      data_.erase(prev_count);
      if (min_count_ == prev_count)
        ++min_count_;
    }
    
    auto& cur_level = data_[prev_count+1];
    cur_level.push_back(Node(prev_count+1, value, key));
    keys_[key] = prev(cur_level.end());
    
    return value;
  }
    
  void put(int key, int value) {
    if (!capacity_)
      return;
    if (keys_.find(key) == keys_.end()) {
      if (keys_.size() >= capacity_) {
        while (data_.find(min_count_) == data_.end())
          ++min_count_;
        auto& level = data_[min_count_];
        const int key = level.front().key_;
        keys_.erase(key);
        level.erase(level.begin());
        if (level.empty()) {
          data_.erase(min_count_);
        }
      }
      data_[0].push_back(Node(0, value, key));
      keys_[key] = data_[0].begin();
      min_count_ = 0;
    }
    auto& it = keys_[key];
    const int prev_count = it->count_;
    data_[prev_count].erase(it);
    if (data_[prev_count].empty()) {
      data_.erase(prev_count);
      if (min_count_ == prev_count)
        ++min_count_;
    }
    auto& cur_level = data_[prev_count+1];
    cur_level.push_back(Node(prev_count+1, value, key));
    keys_[key] = prev(cur_level.end());
    
  }
};

// Method 2
class LFUCache {
public:
    class VALUE{
        public:
        int _value;
        int _cnt;
        list<int>::iterator _listItr;
        VALUE(int v, int c, list<int>::iterator list_itr) {
            _value = v;
            _cnt = c;
            _listItr = list_itr;
        }
    };
    
    int _capacity;
    int _minCnt;
    unordered_map<int, VALUE*> _recKey;
    unordered_map<int, list<int>> _hirarchCache;
    
    LFUCache(int capacity) {
        _capacity = capacity;
        _minCnt = 0;
    }
    
    int get(int key) {
        if (_recKey.find(key) == _recKey.end() || _capacity <= 0) {
            return -1;
        }
        
        auto cur_node = _recKey[key];
        _hirarchCache[cur_node->_cnt].erase(cur_node->_listItr);
        if (_minCnt == cur_node->_cnt && !_hirarchCache[cur_node->_cnt].size()) {
            ++_minCnt;
        }
        cur_node->_cnt++;
        _hirarchCache[cur_node->_cnt].push_front(key);
        _recKey[key] = new VALUE(cur_node->_value, cur_node->_cnt, _hirarchCache[cur_node->_cnt].begin());
        
        return cur_node->_value;
    }
    
    void put(int key, int value) {
        if (_capacity <= 0) {
            return;
        }
        if (_recKey.find(key) == _recKey.end() && _recKey.size() < _capacity) {
            _hirarchCache[1].push_front(key);
           VALUE* new_value = new VALUE(
                value,
                1,
                _hirarchCache[1].begin()
            );
            _recKey.insert(make_pair(key, new_value));
            _minCnt = 1;
            return;
        }
        
        if (_recKey.find(key) != _recKey.end()) {
            auto cur_node = _recKey[key];
            _hirarchCache[cur_node->_cnt].erase(cur_node->_listItr);
            if (_minCnt == cur_node->_cnt && !_hirarchCache[cur_node->_cnt].size()) {
                ++_minCnt;
            }
            cur_node->_cnt++;
            _hirarchCache[cur_node->_cnt].push_front(key);
            _recKey[key] = new VALUE(
                value, 
                cur_node->_cnt,                       
                _hirarchCache[cur_node->_cnt].begin());
            return;
        }
        
        auto cur_layer = _hirarchCache[_minCnt];
        int rm_key = cur_layer.back();
        
        _hirarchCache[_minCnt].pop_back();
        _recKey.erase(rm_key);
        _minCnt = 1;
        _hirarchCache[1].push_front(key);
        _recKey[key] = new VALUE(
               value,
               1,
               _hirarchCache[1].begin()
        );
    }
};
// 529. Minesweeper
vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {
  const int dirs[2][8] = { {-1, 0, 1, 1, 1, 0, -1, -1}, {-1, -1, -1, 0, 1, 1, 1, 0} };
  
  queue<pair<int, int>> processing;
  processing.push(make_pair(click[0], click[1]));
  while (!processing.empty()) {
    const auto [row, col] = processing.front();
    processing.pop();
    if (board[row][col] == 'M') {
      board[row][col] = 'X';
      return board;
    } else if (board[row][col] == 'E') {
      int cnt_mine = 0;
      vector<int> empty;
      for (int i = 0; i < 8; ++i) {
        const int new_row = row + dirs[1][i];
        const int new_col = col + dirs[0][i];
        if (new_row < 0 || new_col < 0 || new_row >= board.size() ||
              new_col >= board[0].size()) continue;
        if (board[new_row][new_col] == 'M') ++cnt_mine;
        else if (board[new_row][new_col] == 'E') 
          empty.push_back(i);
      }
      if (!cnt_mine) {
        board[row][col] = 'B';
        for (const auto& idx : empty) {
          processing.push(make_pair(dirs[1][idx] + row, dirs[0][idx] + col));
        }
      } else {
        board[row][col] = '0' + cnt_mine;
      }  
    }
  }
  return board;
}
// 1230. Toss Strange Coins
double probabilityOfHeads(vector<double>& prob, int target) {
  vector<double> join_probs(prob.size()+1, 0.0);
  join_probs[0] = 1.0;
  for (int i = 0; i < prob.size(); ++i) {
    vector<double> tmp = join_probs;
    for (int j = 0; j <= i + 1; ++j) {
      join_probs[j] = tmp[j] * (1.0 - prob[i]) + (j > 0? tmp[j-1] * prob[i]:0.0);
    }
  }
  return join_probs[target];
}
// 817. Linked List Components
int numComponents(ListNode* head, vector<int>& nums) {
  unordered_set<int> record(begin(nums), end(nums));
  int num = 0;
  bool has_last = false;
    
  while (head) {
    if (record.count(head->val) > 0) {
      if (!has_last) {
        has_last = true;
        ++num;
      }
    } else {
      has_last = false;
    }
    head = head->next;
  }
    
  return num;
}
// 273. Integer to English Words
vector<string> one{"", "One", "Two", "Three", "Four", "Five",
                    "Six", "Seven", "Eight", "Nine", "Ten"};
vector<string> two_1{"Eleven", "Twelve", "Thirteen",
                   "Fourteen", "Fifteen", "Sixteen",
                   "Seventeen", "Eighteen", "Nineteen"};
vector<string> two_2{"", "", "Twenty", "Thirty", "Forty",
                  "Fifty", "Sixty", "Seventy",
                  "Eighty", "Ninety"}; 
vector<string> large{"", "", "Hundred", "Thousand", "Million", "Billion"};
  
string numberToWords(int num) {
  if (!num) return "Zero";
  
  int two = num % 100;
  num /= 100;
  string ans;
  if (two <= 10) {
    ans = one[two];
  } else {
    if (two < 20) {
      ans = two_1[two - 11];
    } else {
      const int low = two%10;
      const int high = two/10;
      string add_space = low?" ":"";
      ans = two_2[high] + add_space + one[low];
    }
  }
  string unit = large[2];
  if (num > 0) {
    const int low = num % 10;
    num = num/10;
    string add_space = ans.empty()?"":" ";
    ans = (low > 0?one[low] + " " + unit + add_space: "") + ans;
  }
  
  for (int i = 3; i <= 5; ++i) {
    unit = large[i];
    if (num > 0) {
      const int low = num % 1000;
      num /= 1000;
      const auto res = low > 0?numberToWords(low):"";
      if (!res.empty()) {
        string add_space = ans.empty()?"":" ";
        ans = res + " " + unit + add_space + ans;
      }
    }
  }
    
  return ans;
}
// 694. Number of Distinct Islands
bool SameIsland(const vector<int>& cur_traj, const vector<vector<int>>& target) {
  int len = 0;
  queue<pair<int, int>> que;
  for (int i = 0; i < target.size(); ++i) {
    que.push(make_pair(i, 50));
  }
  while (!que.empty()) {
    const int cur_que = que.size();
    bool has_same = false;
    for (int i = 0; i < cur_que; ++i) {
      const auto [idx, pos] = que.front();
      que.pop();
      if (pos == cur_traj[len]) {
        has_same = true;
        que.push(make_pair(idx, len+1 >= cur_traj.size()?-1:target[idx][len+1]));
      }
    }
    if (has_same)
      ++len;
    if (len >= cur_traj.size())
      break;
  }
  return len == cur_traj.size()?true:false;
}  
  
int numDistinctIslands(vector<vector<int>>& grid) {
  unordered_map<int, vector<vector<int>>> trajectory;
  
  const int shift = 50;
  const int dirs[2][4] = { {0, 1, 0, -1}, {-1, 0, 1, 0} };
  for (int i = 0; i < grid.size(); ++i) {
    for (int j = 0; j < grid[0].size(); ++j) {
      if (grid[i][j]) {
        int num = 0;
        vector<int> cur_traj;
        grid[i][j] = 0;
        queue<pair<int, int>> que({ {i, j} });
        while (!que.empty()) {
          const auto [row, col] = que.front();
          que.pop(); 
          ++num;
          cur_traj.emplace_back((row - i) * 100 + j - col + 50);
          for (int k = 0; k < 4; ++k) {
            const int nr = row + dirs[1][k];
            const int nc = col + dirs[0][k];
            if (nr < 0 || nc < 0 || nr >= grid.size() || 
                nc >= grid[0].size() || !grid[nr][nc]) {
              continue;
            }
            que.push(make_pair(nr, nc));
            grid[nr][nc] = 0;
          }
        }
        if (!trajectory.count(num)) {
          trajectory[num] = {cur_traj};
        } else if (!SameIsland(cur_traj, trajectory[num])) {
          trajectory[num].push_back(cur_traj);
        }
      }
    }
  }
  
  int ans = 0;
  for (const auto& [size, eles] : trajectory) {
    ans += eles.size();
  }
  
  return ans;
}
// String add
// 415. Add Strings & decresing & multiply
string addStrings(string num1, string num2) {
  int cf = 0;
  string ans;
  int idx1 = num1.size() - 1;
  int idx2 = num2.size() - 1;
  while (cf || idx1 >= 0 || idx2 >= 0) {
    int cur = 0;
    cur += idx1 >= 0? num1[idx1] - '0':0;
    cur += idx2 >= 0? num2[idx2] - '0':0;
    cur += cf;
    --idx1;
    --idx2;
    
    ans = to_string(cur%10) + ans; 
    
    cf = cur/10;
  }
  
  return ans;
}
// 394. Decode String
string Repeat(const string& str, const int num) {
  if (num == 1) return str;
  if (!num) return "";
  
  return Repeat(str, num/2) + Repeat(str, num/2) + (num&1?str:"");
} 
  
string decodeString(string s, const int stt = 0) {
  string ans;
  int num = 0;
  for (int i = stt; i < s.size(); ++i) {
    if (s[i] >= 'a' && s[i] <= 'z') {
      ans += s[i];
    } else if (s[i] >= '0' && s[i] <= '9') {
      num = num * 10 + s[i] - '0';
    } else if (s[i] == '[') {
      int cnt = 1;
      const int left = i;
      while (cnt) {
        ++i;
        if (s[i] == ']')
          --cnt;
        if (s[i] == '[')
          ++cnt;
      }
      const string new_str = decodeString(s.substr(left+1, i - left - 1));
      ans += Repeat(new_str, num);
      num = 0;
    }
  }
  
  return ans;
}
/*
题目是parse yaml file 和 implement query method,每个indentation是两个空格

"""
k1:v1
k2:
  k21:v21
  k22:v22
  k23:
    k231:v231
  k24:v24
k3:
  k31:v31
"""

query("k1") => "v1"
query("k2.k23.k231") => "v231"
*/
class YamlFile {
 private:
  std::unordered_map<std::string, std::unordered_set<std::string>> graph_;
  std::unordered_map<std::string, std::string> values_;
  std::string start_ = "";
  
 public:
  int Parse(std::vector<std::vector<std::string>>& yaml_strs, 
             const std::string& parent = start, const int cur_space = 0,
             const int cur_index = 0) {
    for (int i = cur_index; i < yaml_strs.size(); ) {
      bool has_value = false;
      string key;
      string value;
      const int new_space = CountStatus(yaml_strs[i], &has_value, &key, &value);
      if (new_space == cur_space + 2) {
        graph_[parent].insert(key);
        if (has_value) {
          values_[key] = value;
          ++i;
        } else {
          i = Parse(yaml_strs, yaml_strs[i], new_space, i + 1);  
        }
      } else return i;
    }
    
    return yaml_strs.size();
  }
  
  std::string Query(const std::string& query) {
    const std::vector<std::string> path = Split(query);
    std::string cur = path[0];
    for (int i = 1; i < path.size(); ++i) {
      if (graph_.count(cur) > 0 && graph_[cur].count(path[i]) > 0) {
        cur = path[i];
      } else {
        return "";
      }
    }
    return values_.count(cur) > 0?values_[cur]:"";
  }
};