力扣周赛 347

126 阅读4分钟

力扣周赛 347

1. 移除字符串中的尾随零

1.1 题目

给你一个用字符串表示的正整数 num ,请你以字符串形式返回不含尾随零的整数 num 。

示例 1:

输入:num = "51230100" 输出:"512301" 解释:整数 "51230100" 有 2 个尾随零,移除并返回整数 "512301" 。 示例 2:

输入:num = "123" 输出:"123" 解释:整数 "123" 不含尾随零,返回整数 "123" 。

提示:

1 <= num.length <= 1000 num 仅由数字 0 到 9 组成 num 不含前导零

1.2 题解

从后往前遍历,遇到第一个不是 0 的字符,就返回前面的字符串。

1.3 代码

class Solution {
public:
    string removeLeadingZeros(string num) {
        int n = num.size();
        int i = n - 1;
        while (i >= 0 && num[i] == '0') {
            i--;
        }
        return num.substr(0, i + 1);
    }
};

1.4 复杂度分析

时间复杂度:O(n)

空间复杂度:O(1)

2. 对角线上不同值的数量差

2.1 题目

给你一个下标从 0 开始、大小为 m x n 的二维矩阵 grid ,请你求解大小同样为 m x n 的答案矩阵 answer 。

矩阵 answer 中每个单元格 (r, c) 的值可以按下述方式进行计算:

令 topLeft[r][c] 为矩阵 grid 中单元格 (r, c) 左上角对角线上 不同值 的数量。 令 bottomRight[r][c] 为矩阵 grid 中单元格 (r, c) 右下角对角线上 不同值 的数量。 然后 answer[r][c] = |topLeft[r][c] - bottomRight[r][c]| 。

返回矩阵 answer 。

矩阵对角线 是从最顶行或最左列的某个单元格开始,向右下方向走到矩阵末尾的对角线。

如果单元格 (r1, c1) 和单元格 (r, c) 属于同一条对角线且 r1 < r ,则单元格 (r1, c1) 属于单元格 (r, c) 的左上对角线。类似地,可以定义右下对角线。

示例 1: 输入:grid = [[1,2,3],[3,1,5],[3,2,1]] 输出:[[1,1,0],[1,0,1],[0,1,1]]

2.2 题解

我们可以用两个数组来表示左上角和右下角的对角线上的不同值的数量,然后再遍历一遍,计算答案。

2.3 代码

class Solution {
public:
    vector<vector<int>> diagonalDifference(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int>> topLeft(m, vector<int>(n));
        vector<vector<int>> bottomRight(m, vector<int>(n));
        for (int i = 0; i < m; i++) {
            int j = 0;
            int k = i;
            int cnt = 0;
            while (j < n && k < m) {
                cnt |= (1 << grid[k][j]);
                topLeft[k][j] = __builtin_popcount(cnt);
                j++;
                k++;
            }
        }
        for (int j = 1; j < n; j++) {
            int i = 0;
            int k = j;
            int cnt = 0;
            while (i < m && k < n) {
                cnt |= (1 << grid[i][k]);
                topLeft[i][k] = __builtin_popcount(cnt);
                i++;
                k++;
            }
        }
        for (int i = 0; i < m; i++) {
            int j = n - 1;
            int k = i;
            int cnt = 0;
            while (j >= 0 && k < m) {
                cnt |= (1 << grid[k][j]);
                bottomRight[k][j] = __builtin_popcount(cnt);
                j--;
                k++;
            }
        }
        for (int j = n - 2; j >= 0; j--) {
            int i = 0;
            int k = j;
            int cnt = 0;
            while (i < m && k >= 0) {
                cnt |= (1 << grid[i][k]);
                bottomRight[i][k] = __builtin_popcount(cnt);
                i++;
                k--;
            }
        }
        vector<vector<int>> ans(m, vector<int>(n));
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                ans[i][j] = abs(topLeft[i][j] - bottomRight[i][j]);
            }
        }
        return ans;
    }
};

2.4 复杂度分析

时间复杂度:O(mn)

空间复杂度:O(mn)

3. 使所有字符相等的最小成本

3.1 题目

给你一个下标从 0 开始、长度为 n 的二进制字符串 s ,你可以对其执行两种操作:

选中一个下标 i 并且反转从下标 0 到下标 i(包括下标 0 和下标 i )的所有字符,成本为 i + 1 。 选中一个下标 i 并且反转从下标 i 到下标 n - 1(包括下标 i 和下标 n - 1 )的所有字符,成本为 n - i 。 返回使字符串内所有字符 相等 需要的 最小成本 。

反转 字符意味着:如果原来的值是 '0' ,则反转后值变为 '1' ,反之亦然。

示例 1:

输入:s = "0011" 输出:2 解释:执行第二种操作,选中下标 i = 2 ,可以得到 s = "0000" ,成本为 2 。可以证明 2 是使所有字符相等的最小成本。

3.2 题解

用贪心算法来解决这个问题。首先,我们需要找到字符串中出现次数最多的字符,然后将其余字符转换为该字符。我们可以通过计算字符串中每个字符的出现次数来找到出现次数最多的字符。然后,我们可以计算将所有字符转换为该字符所需的成本。这是一个简单的贪心策略,因为我们总是选择出现次数最多的字符。

这里有一个例子:假设字符串是“00110”。出现次数最多的字符是“0”,它出现了 2 次。因此,我们将所有“1”转换为“0”,并计算成本。第一个操作将“00110”转换为“00010”,成本为 3(反转前三个字符)。第二个操作将“00010”转换为“00000”,成本为 2(反转后两个字符)。因此,总成本为 5。

3.3 代码

def minCost(s: str) -> int:
    n = len(s)
    cnt = [0] * 2
    for i in range(n):
        cnt[ord(s[i]) - ord('0')] += 1
    return min(cnt[0] + sum(1 for i in range(n) if s[i] == '1'),
               cnt[1] + sum(1 for i in range(n) if s[i] == '0'))

复杂度分析

时间复杂度:O(n)

空间复杂度:O(n)

4. 矩阵中严格递增的单元格数

4.1 题目

给你一个下标从 1 开始、大小为 m x n 的整数矩阵 mat,你可以选择任一单元格作为 起始单元格 。

从起始单元格出发,你可以移动到 同一行或同一列 中的任何其他单元格,但前提是目标单元格的值 严格大于 当前单元格的值。

你可以多次重复这一过程,从一个单元格移动到另一个单元格,直到无法再进行任何移动。

请你找出从某个单元开始访问矩阵所能访问的 单元格的最大数量 。

返回一个表示可访问单元格最大数量的整数。

示例 1:

输入:mat = [[3,1],[3,4]] 输出:2 解释:上图展示了从第 1 行、第 2 列的单元格开始,可以访问 2 个单元格。可以证明,无论从哪个单元格开始,最多只能访问 2 个单元格,因此答案是 2

4.2 题解

深度优先搜索(DFS)来解决这个问题。从每个单元格开始,你可以使用 DFS 来探索所有可能的路径。在搜索过程中,你可以记录每个单元格的最长递增路径长度。如果你已经访问过一个单元格,那么你可以直接返回该单元格的最长递增路径长度。

这个问题是一个经典的动态规划问题。我们可以使用记忆化搜索来避免重复计算。我们可以使用一个二维数组 dp 来记录每个单元格的最长递增路径长度。如果我们已经计算了一个单元格的最长递增路径长度,那么我们可以直接返回该值。

4.3 代码

def dfs(mat, dp, i, j):
    if dp[i][j] != -1:
        return dp[i][j]
    dp[i][j] = 1
    for di, dj in [[0, 1], [1, 0], [0, -1], [-1, 0]]:
        ni = i + di
        nj = j + dj
        if 0 <= ni < len(mat) and 0 <= nj < len(mat[0]) and mat[ni][nj] > mat[i][j]:
            dp[i][j] = max(dp[i][j], dfs(mat, dp, ni, nj) + 1)
    return dp[i][j]
    
def maxIncreasingCells(mat: List[List[int]]) -> int:
    dp = [[-1] * len(mat[0]) for _ in range(len(mat))]
    ans = 0
    for i in range(len(mat)):
        for j in range(len(mat[0])):
            ans = max(ans, dfs(mat, dp, i, j))
    return ans