LeetCode 第204场 周赛 题解!!!!

第 204 场周赛

全套周赛题解新鲜出炉!

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

思路:遍历

签到题。第一层循环遍历子数组起点,第二层循环遍历子数组终点之后的部分,遍历 k k k 次,若不匹配则返回 f a l s e false false,第二层循环遍历完成后说明匹配成功,返回 t r u e true true。否则继续匹配新的子数组,第一层循环遍历结束后还没有返回说明未匹配,返回 f a l s e false false

代码:

class Solution {
public:
    bool containsPattern(vector<int>& arr, int m, int k) {
        int n = arr.size();
        for (int i = 0; i + m * k <= n; ++i) {
            bool check = true;
            for (int j = i + m; j < i + m * k; ++j) {
                if (arr[j] != arr[j - m]) {
                    check = false;
                    break;
                }
            }
            if (check) {
                return true;
            }
        }
        return false;
    }
};

复杂度分析:

时间复杂度为 O ( N 2 ) O(N^2) O(N2),空间复杂度为 O ( 1 ) O(1) O(1)

全套周赛题解新鲜出炉!

题目2:5500. 乘积为正数的最长子数组长度

思路:分治 + 前缀和

首先子数组含0肯定不满足条件,所以遍历一遍整个数组,通过 0 出现的位置将数组分为若干个子数组,每个子数组调用 d f s dfs dfs 函数进行分治计算。

根据题意,只要某一段区间内负数出现的次数为偶数,该区间即满足乘积为正数。所以先记录每一个子数组第一次出现负数的位置,然后用类似于前缀和的方式,记录子数组遍历到每一个位置时,该位置之前出现负数的数量,更新全局最优值即可。

代码:

class Solution {
public:
    int res = 0;
    
    void dfs(int l, int r, vector<int>& a) {
        int f = -1;	//记录第一次出现负数位置
        int cnt = 0;
        for(int i = l; i <= r; i++) {
            if(a[i] < 0) {	// 记录截至目前,负数出现次数奇偶性
                cnt = cnt ^ 1;
            }
            if(cnt == 0) {	
                // 出现偶数次,从子数组起始点到当前位置满足条件
                res = max(res, i - l + 1);
            } else {	
                // 出现奇数次,从第一次出现负数位置到当前位置满足条件
                if(f == -1) f = i;
                if(f != -1) res = max(res, i - f);
            }
        }
    }
    
    int getMaxLen(vector<int>& a) {
        int l = 0, r = a.size() - 1;
        for(int i = 0; i < a.size(); i++) {
            if(a[i] == 0) {
                dfs(l, i - 1, a);
                l = i + 1;
            }
        }
        dfs(l, r, a);
        return res;
    }
};

复杂度分析:

时间复杂度为 O ( N ) O(N) O(N),空间复杂度为 O ( N ) O(N) O(N)

全套周赛题解新鲜出炉!

题目3:5501. 使陆地分离的最少天数

思路:DFS

考虑图中左上角的陆地,最多只需要将其下面和右面两块陆地更改为水单元,即可使该陆地块与其他岛屿分离,所以答案最多为2。

首先深搜整个图,计算岛屿数量,若数量不为1,则不需操作就可以满足题意,返回 0 即可;

其次假设删除某一块陆地单元可以满足题意,遍历每一个陆地单元,将其删除后深搜整个图,计算岛屿数量,满足条件则返回 1 即可。

若遍历完所有陆地单元都无法满足题意,则说明需要操作两次,返回 2.

代码:

class Solution {
public:
    int n, m;
    vector<vector<bool>> st;
    vector<vector<int>> g;
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

    void dfs(int x, int y) {
        st[x][y] = true;
        for (int i = 0; i < 4; i ++ ) {
            int a = x + dx[i], b = y + dy[i];
            if (a >= 0 && a < n && b >= 0 && b < m && !st[a][b] && g[a][b] == 1)
                dfs(a, b);
        }
    }

    bool check() {	// 判断岛屿块数量
        int cnt = 0;
        st = vector<vector<bool>>(n, vector<bool>(m));
        for (int i = 0; i < n; i ++ )
            for (int j = 0; j < m; j ++ )
                if (!st[i][j] && g[i][j] == 1) {
                    cnt ++ ;
                    dfs(i, j);
                }
        return cnt > 1;
    }

    int minDays(vector<vector<int>>& grid) {
        n = grid.size(), m = grid[0].size();
        g = grid;

        if (check()) return 0;

        for (int i = 0; i < n; i ++ )
            for (int j = 0; j < m; j ++ )
                if (g[i][j]) {
                    g[i][j] = 0;	//删除某一陆地单元
                    if (check()) return 1;
                    g[i][j] = 1;	//失败则复原
                }

        return 2;
    }
};

复杂度分析:

时间复杂度为 O ( N 2 M 2 ) O(N^2M^2) O(N2M2),空间复杂度为 O ( N M ) O(NM) O(NM)

全套周赛题解新鲜出炉!

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

思路:组合数 + 递归

首先根节点是固定不能变的。

其次,根节点值 k k k 确定后,整个数组可以分为小于 k k k 的部分和大于 k k k 的部分,分别对应左子树和右子树。假设左子树和右子树结点顺序相对固定,那么这两个部分互相交叉是不会影响二叉搜索树的形态的。

举个例子,对于 [ 3 , 4 , 5 , 1 , 2 ] [3, 4, 5, 1, 2] [3,4,5,1,2] 这样的一个序列,确定根节点值 k = 3 k = 3 k=3 后,左子树序列为 [ 1 , 2 ] [1, 2] [1,2] ,右子树序列为 [ 4 , 5 ] [4, 5] [4,5] ,此时这两个部分互相交叉得到的新序列,如 [ 3 , 4 , 1 , 2 , 5 ] [3, 4, 1, 2, 5] [3,4,1,2,5], [ 3 , 1 , 2 , 4 , 5 ] [3, 1, 2, 4, 5] [3,1,2,4,5] 等形成的二叉搜索树都是相同的。

所以,这样的交叉方案数其实就是在 n − 1 n - 1 n1 个位置中,寻找 k − 1 k - 1 k1 个位置的方案数 C n − 1 k − 1 C_{n-1} ^ {k-1} Cn1k1

下面我们只需要考虑左右子树序列分别可以选取的方案数,而这就相当于原问题缩小规模的子问题,通过递归实现即可。

代码:

typedef long long LL;

class Solution {
public:
    vector<vector<int>> C;
    const int MOD = 1e9 + 7;

    int numOfWays(vector<int>& nums) {
        int n = nums.size();
        C = vector<vector<int>> (n + 1, vector<int>(n + 1));
        for (int i = 0; i <= n; i ++ )
            for (int j = 0; j <= i; j ++ )
                if (!j) C[i][j] = 1;	// 计算组合数
                else C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
		
        // 题目要求除给定以外,所以要减1
        return (f(nums) + MOD - 1) % MOD;  
    }

    int f(vector<int> nums) {
        if (nums.empty()) return 1;
        int n = nums.size();
        int k = nums[0];
        vector<int> left, right;	// 左右子序列
        for (auto x: nums)
            if (x < k) left.push_back(x);
            else if (x > k) right.push_back(x - k);
        return (LL)C[n - 1][k - 1] * f(left) % MOD * f(right) % MOD;
    }
};

复杂度分析:

时间复杂度为 O ( N 2 ) O(N^2) O(N2),空间复杂度为 O ( N 2 ) O(N^2) O(N2)

猜你喜欢

转载自blog.csdn.net/weixin_42396397/article/details/108309107