周赛348(模拟、反向思维、数位DP)

2716. 最小化字符串长度

难度中等3

给你一个下标从 0 开始的字符串 s ,重复执行下述操作 任意 次:

  • 在字符串中选出一个下标 i ,并使 c 为字符串下标 i 处的字符。并在 i 左侧(如果有)和 右侧(如果有)各 删除 一个距离 i 最近 的字符 c

请你通过执行上述操作任意次,使 s 的长度 最小化

返回一个表示 最小化 字符串的长度的整数。

示例 1:

输入:s = "aaabc"
输出:3
解释:在这个示例中,s 等于 "aaabc" 。我们可以选择位于下标 1 处的字符 'a' 开始。接着删除下标 1 左侧最近的那个 'a'(位于下标 0)以及下标 1 右侧最近的那个 'a'(位于下标 2)。执行操作后,字符串变为 "abc" 。继续对字符串执行任何操作都不会改变其长度。因此,最小化字符串的长度是 3 。

示例 2:

输入:s = "cbbd"
输出:3
解释:我们可以选择位于下标 1 处的字符 'b' 开始。下标 1 左侧不存在字符 'b' ,但右侧存在一个字符 'b'(位于下标 2),所以会删除位于下标 2 的字符 'b' 。执行操作后,字符串变为 "cbd" 。继续对字符串执行任何操作都不会改变其长度。因此,最小化字符串的长度是 3 。

示例 3:

输入:s = "dddaaa"
输出:2
解释:我们可以选择位于下标 1 处的字符 'd' 开始。接着删除下标 1 左侧最近的那个 'd'(位于下标 0)以及下标 1 右侧最近的那个 'd'(位于下标 2)。执行操作后,字符串变为 "daaa" 。继续对新字符串执行操作,可以选择位于下标 2 的字符 'a' 。接着删除下标 2 左侧最近的那个 'a'(位于下标 1)以及下标 2 右侧最近的那个 'a'(位于下标 3)。执行操作后,字符串变为 "da" 。继续对字符串执行任何操作都不会改变其长度。因此,最小化字符串的长度是 2 。

提示:

  • 1 <= s.length <= 100
  • s 仅由小写英文字母组成

阅读理解

统计字符串中出现的不同字符数

class Solution:
    def minimizedStringLength(self, s: str) -> int:
        c = Counter(s)
        return len(c)

2717. 半有序排列

难度简单 0

给你一个下标从 0 开始、长度为 n 的整数排列 nums

如果排列的第一个数字等于 1 且最后一个数字等于 n ,则称其为 半有序排列 。你可以执行多次下述操作,直到将 nums 变成一个 半有序排列

  • 选择 nums 中相邻的两个元素,然后交换它们。

返回使 nums 变成 半有序排列 所需的最小操作次数。

排列 是一个长度为 n 的整数序列,其中包含从 1n 的每个数字恰好一次。

示例 1:

输入:nums = [2,1,4,3]
输出:2
解释:可以依次执行下述操作得到半有序排列:
1 - 交换下标 0 和下标 1 对应元素。排列变为 [1,2,4,3] 。
2 - 交换下标 2 和下标 3 对应元素。排列变为 [1,2,3,4] 。
可以证明,要让 nums 成为半有序排列,不存在执行操作少于 2 次的方案。

示例 2:

输入:nums = [2,4,1,3]
输出:3
解释:
可以依次执行下述操作得到半有序排列:
1 - 交换下标 1 和下标 2 对应元素。排列变为 [2,1,4,3] 。
2 - 交换下标 0 和下标 1 对应元素。排列变为 [1,2,4,3] 。
3 - 交换下标 2 和下标 3 对应元素。排列变为 [1,2,3,4] 。
可以证明,要让 nums 成为半有序排列,不存在执行操作少于 3 次的方案。

示例 3:

输入:nums = [1,3,4,2,5]
输出:0
解释:这个排列已经是一个半有序排列,无需执行任何操作。

提示:

  • 2 <= nums.length == n <= 50
  • 1 <= nums[i] <= 50
  • nums 是一个 排列

模拟

简单题,可以直接模拟,或者不需要进行元素的交换,找出最大值和最小值的位置,判断一下是否会交错,交错的话最后答案减一,不交错答案即 最小值到左端点 + 最大值到右端点 的长度

class Solution:
    def semiOrderedPermutation(self, nums: List[int]) -> int:
        ans = 0
        mn, idx = inf, -1
        for i, v in enumerate(nums):
            if mn > v:
                mn = v
                idx = i
        ans += idx
        mx, idxm = -inf, -1
        for i, v in enumerate(nums):
            if mx < v:
                mx = v
                idxm = i
        if idxm < idx:
            ans += len(nums) - 1 - idxm - 1
        else:
            ans += len(nums) - 1 - idxm
        return ans

2718. 查询后矩阵的和

难度中等10

给你一个整数 n 和一个下标从 0 开始的 二维数组 queries ,其中 queries[i] = [typei, indexi, vali]

一开始,给你一个下标从 0 开始的 n x n 矩阵,所有元素均为 0 。每一个查询,你需要执行以下操作之一:

  • 如果 typei == 0 ,将第 indexi 行的元素全部修改为 vali ,覆盖任何之前的值。
  • 如果 typei == 1 ,将第 indexi 列的元素全部修改为 vali ,覆盖任何之前的值。

请你执行完所有查询以后,返回矩阵中所有整数的和。

示例 1:

输入:n = 3, queries = [[0,0,1],[1,2,2],[0,2,3],[1,0,4]]
输出:23
解释:上图展示了每个查询以后矩阵的值。所有操作执行完以后,矩阵元素之和为 23 。

示例 2:

输入:n = 3, queries = [[0,0,4],[0,1,2],[1,0,1],[0,2,3],[1,2,1]]
输出:17
解释:上图展示了每一个查询操作之后的矩阵。所有操作执行完以后,矩阵元素之和为 17 。

提示:

  • 1 <= n <= 104
  • 1 <= queries.length <= 5 * 104
  • queries[i].length == 3
  • 0 <= typei <= 1
  • 0 <= indexi < n
  • 0 <= vali <= 105

倒序遍历(正难则反)

class Solution {
    public long matrixSumQueries(int n, int[][] queries) {
        // 正序不好做,考虑倒序
        // 倒序遍历,记录下这次修改能影响下的 行数 和 列数, 每次修改后影响数 - 1
        long ans = 0l;
        // 记录一下当前行列有没有被修改过
        int[] arrrow = new int[n];
        int[] arrcol = new int[n];
        // 这一次操作能影响的 行/列 数
        int row = n, col = n;
        for(int i = queries.length-1; i >= 0; i--){
            int type = queries[i][0], index = queries[i][1], val = queries[i][2];
            if(type == 0){ // 修改整行
                if(arrrow[index] == 1) 
                    continue; // 这一行已经被后面的操作修改过了
                ans += (long)(val * row); // 答案 += val * 还能影响的行数
                col -= 1; // 影响列数 - 1
                arrrow[index] = 1;
            }else{
                if(arrcol[index] == 1)
                    continue;
                ans += (long)(val * col);
                row -= 1;
                arrcol[index] = 1;
            }
        }
        return ans;
    }
}

2719. 统计整数数目

难度困难4

给你两个数字字符串 num1num2 ,以及两个整数 max_summin_sum 。如果一个整数 x 满足以下条件,我们称它是一个好整数:

  • num1 <= x <= num2
  • min_sum <= digit_sum(x) <= max_sum.

请你返回好整数的数目。答案可能很大,请返回答案对 109 + 7 取余后的结果。

注意,digit_sum(x) 表示 x 各位数字之和。

示例 1:

输入:num1 = "1", num2 = "12", min_num = 1, max_num = 8
输出:11
解释:总共有 11 个整数的数位和在 1 到 8 之间,分别是 1,2,3,4,5,6,7,8,10,11 和 12 。所以我们返回 11 。

示例 2:

输入:num1 = "1", num2 = "5", min_num = 1, max_num = 5
输出:5
解释:数位和在 1 到 5 之间的 5 个整数分别为 1,2,3,4 和 5 。所以我们返回 5 。

提示:

  • 1 <= num1 <= num2 <= 1022
  • 1 <= min_sum <= max_sum <= 400

数位DP

class Solution {
    private static final int MOD = (int)1e9 + 7;
    int min_sum, max_sum;
    int[][] cache;
    public int count(String num1, String num2, int min_sum, int max_sum) {
        this.min_sum = min_sum;
        this.max_sum = max_sum;
        int m = num2.toCharArray().length;
        cache = new int[m][max_sum + 1];
        for(int i = 0; i < m; i++){
            Arrays.fill(cache[i], -1);
        }
        char[] s2 = num2.toCharArray();
        int a = dfs(0, 0, true, s2);
        for(int i = 0; i < m; i++){
            Arrays.fill(cache[i], -1);
        }
        char[] s1 = num1.toCharArray();
        s1[s1.length - 1]--;
        int b = dfs(0, 0, true, s1);
        //  b-a 可能是负数(注意取模后谁大谁小就不一定了)
        return (a - b + MOD) % MOD;
    }

    public int dfs(int i, int sum, boolean isLimit, char[] s){
        if(i == s.length){
            return sum >= min_sum ? 1 : 0;
        }
        if(!isLimit && cache[i][sum] >= 0) return cache[i][sum];
        int res = 0;
        int up = isLimit ? s[i] - '0' : 9;
        for(int d = 0; d <= up; d++){
            if((sum + d) <= max_sum)
                res = (res + dfs(i+1, sum+d, isLimit & (d == up), s)) % MOD;
        }
        if(!isLimit) cache[i][sum] = res % MOD;
        return res % MOD;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值