LeetCode 力扣周赛 204

81 阅读2分钟

5499. 重复至少 K 次且长度为 M 的模式

知识点:枚举

这道题太直白了,没啥花里胡哨的东西,直接枚举起点,然后检查是否至少有 K 段重复即可。

class Solution {
 public:
  bool containsPattern(vector<int>& arr, int m, int k) {
    for(int i = 0; i+m <= arr.size(); i++) {
      int pre = i+m;
      int hit = 0;
      while(pre+m <= arr.size()) {
        bool flag = true;
        for(int p = 0; p < m; p++) {
          if(arr[pre+p] != arr[i+p]) {
            flag = false;
            break;
          }
        }
        if(flag) {
          hit++;
          pre += m;
        } else {
          break;
        }
        if(hit+1 >= k) {
          return true;
        }
      }
    }
    return false;
  }
};

5500. 乘积为正数的最长子数组长度

知识点:递推

设 POS[i] 是以 nums[i] 结尾,乘积为正的最长子数组的长度。

设 NEG[i] 是以 nums[i] 结尾,乘积为负的最长子数组的长度。

为了编写代码方便,设 nums 下标从 1 开始。那么 POS[0] = NEG[0] = 0。

接下来讨论一下 nums[i] 的值如何影响 POS[i] 及 NEG[i] 的计算。

  • 当 nums[i] 为 0 时,显然有 POS[i] = NEG[i] = 0,即这样的子数组不存在。
  • 当 nums[i] 为正时:
    • POS[i] = POS[i-1] + 1。
    • NEG[i] = NEG[i-1] ? (NEG[i-1] + 1) : 0。
    • 为何计算NEG[i]时要判断 NEG[i-1] 不为 0 ?因为 nums[i] 自己没法成为一个乘积为负的数组。
  • 当 nums[i] 为负时:
    • POS[i] = NEG[i-1] ? (NEG[i-1] + 1) : 0。 判断 NEG[i-1] 是否为 0 的原因同上。
    • NEG[i] = POS[i-1] + 1;
int dp[100001][2]; // dp[i][0] 即 POS,dp[i][1] 即 NEG。
class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        dp[0][0] = dp[0][1] = 0;
        int anw = 0;
        for(int i = 1; i <= nums.size(); i++) {
            if(nums[i-1] == 0) {
                dp[i][0] = 0;
                dp[i][1] = 0;
            } else if(nums[i-1] > 0) {
                dp[i][0] = dp[i-1][0] + 1;
                dp[i][1] = dp[i-1][1] ? (dp[i-1][1] + 1) : 0;
            } else {
                dp[i][0] = dp[i-1][1] ? (dp[i-1][1] + 1) : 0;
                dp[i][1] = dp[i-1][0] + 1;
            }
            
            anw = max(anw, dp[i][0]);
        }
        return anw;
    }
};

5501. 使陆地分离的最少天数

知识点:BFS,思维题

有个比较搞喜的地方:最多删除两次必可使陆地分离。

每个格子最多会有四条边和其他格子相连。在边上的格子最多有三条边。在角上的最多有两条边。无论岛屿长成什么样子,肯定是会有角的,所以最多只需删除两次。

首先,判断输入本身就是分离的。 其次,暴力枚举删除输入中的一个 1,然后判断是否分离。 再其次,直接返回 2 就 ok 啦~

class Solution {
 public:
  bool check(const vector<vector<int>>& grid) {
    int x = 0, y = 0;
    int cnt = 0;
    for(int i = 0; i < grid.size(); i++) {
      for(int j = 0; j < grid[i].size(); j++) {
        if(grid[i][j] == 0) continue;
        cnt++;
        x = i;
        y = j;
      }
    }
    if(cnt == 0) {
      return true;
    }
    queue<pair<int, int>> q;
    bool mark[30][30] = {0};
    q.push(make_pair(x, y));
    mark[x][y] = true;
    cnt--;
    while(q.empty() == false) {
      auto f = q.front();
      q.pop();
      int dx[] = {-1,  1, 0, 0};
      int dy[] = { 0, 0, -1, 1};
      for(int i = 0; i < 4; i++) {
        int nx = dx[i] + f.first;
        int ny = dy[i] + f.second;
        if(0 <= nx && nx < grid.size() && 0 <= ny && ny < grid[0].size() && grid[nx][ny] == 1) {
          auto p = make_pair(nx, ny);
          if(mark[nx][ny]) { continue; }
          mark[nx][ny] = true;
          q.push(p);
          cnt--;
        }
      }
    }
    return cnt != 0;
  }
  int minDays(vector<vector<int>>& grid) {
    if(check(grid)) {
      return 0;
    }
    for(int i = 0; i < grid.size(); i++) {
      for(int j = 0; j < grid[0].size(); j++) {
        if(grid[i][j] == 0) {
          continue;
        }
        grid[i][j] = 0;
        if(check(grid)) {
          return 1;
        }
        grid[i][j] = 1;
      }
    }
    return 2;
  }
};

5502. 将子数组重新排序得到同一个二叉查找树的方案数

知识点:二叉搜索树;排列组合

二叉查找树,又称二叉排序树,二叉搜索树。其特点是,右子树中的元素都小于根节点,左子树的元素都大于根节点,且左右子树也都是二叉搜索树。当构造一棵二叉搜索树时,第一个插入的元素必然是根节点,其后插入的元素根据与根节点的大小关系被插入到左子树或右子树。

由此可知,如果两种排列对应的二叉搜索树相同,那么必然第一个元素是相同的。

设,小于第一个的元素构成的序列为 less,大于第一个的元素构成的序列为 greater。在不修改 less,greater 内部顺序的前提下,调整 less + greater 这个大序列的顺序,就能得到一个可以构造相同二叉树的新序列。

换个说法,less 的顺序确定了元素插入左子树的顺序,同样的,greater 确定了元素插入右子树的顺序。至于,是先构造左子树还是构造右子树,并不重要。所以 less + greater 的顺序可以调整。

那为何 less 和 greater 的内部顺序不能调整呢?其实不是不能调整,而是要放到构造左右子树的时候再去调整

那么,还剩最后一个问题,less + greater,一共有多少符合要求排列方式呢?答案为 Cless.size()+greater.size()less.size()C^{less.size()}_{less.size() + greater.size()}。也就是说,一共有 x 个坑,先选出一部分放 less,剩下的放 greater

最后将每个子树的组合数累乘即可。

const int64_t mod = 1000000007;
class Solution {
public:
    int64_t com[1001][1001];
    int64_t combine(int a, int b) {
        //cout << a << " " << b << " " << com[a][b] << endl;
        return com[a][b];
    }
    void dfs(const vector<int> &num, int L, int R, int64_t &mul) {
        if(R-L+1 <= 2) {
            return;
        }
        vector<int> less, greater;
        for(int i = L+1; i <= R; i++) {
            if(num[i] < num[L]) {
                less.push_back(num[i]);
            } else {
                greater.push_back(num[i]);
            }
        }
        mul *= combine(greater.size() + less.size(), greater.size());
        if(mul >= mod) {
            mul %= mod;
        }
        dfs(less, 0, less.size()-1, mul);
        dfs(greater, 0, greater.size()-1, mul);
    }
    int numOfWays(vector<int>& nums) {
        //C(n,m)=C(n-1,m)+C(n-1,m-1)
        com[1][0]=com[1][1]=1;
        for(int i=2;i<=1000;i++){
            com[i][0]=1;
            for(int j=1;j<=i;j++)
                com[i][j]=(com[i-1][j]+com[i-1][j-1]) % mod;
        }
        
        int64_t mul = 1;
        dfs(nums, 0, nums.size()-1, mul);
        return (mul - 1 + mod) % mod;
    }
};