力扣第216场周赛思路分享(含代码)

0. 写在前面

第一次参加力扣的周赛,原因很简单,看到了商汤,以及这周日我终于没课了。整体下来感觉还可以,不过做完就看到讨论区说简单,果然我只配好好活着。这里只是分享一下我第一时间的思路,没有经过详细的优化,有各种建议欢迎提出。
在这里插入图片描述

1. 检查两个字符串数组是否相等

(1)题目

给你两个字符串数组 word1 和 word2 。如果两个数组表示的字符串相同,返回 true ;否则,返回 false 。

数组表示的字符串 是由数组中的所有元素 按顺序 连接形成的字符串。

示例 1:
输入:word1 = ["ab", "c"], word2 = ["a", "bc"]
输出:true
解释:
word1 表示的字符串为 "ab" + "c" -> "abc"
word2 表示的字符串为 "a" + "bc" -> "abc"
两个字符串相同,返回 true
示例 2:
输入:word1 = ["a", "cb"], word2 = ["ab", "c"]
输出:false
示例 3:
输入:word1  = ["abc", "d", "defg"], word2 = ["abcddefg"]
输出:true
提示:
1 <= word1.length, word2.length <= 10^3
1 <= word1[i].length, word2[i].length <= 10^3
1 <= sum(word1[i].length), sum(word2[i].length) <= 10^3
word1[i] 和 word2[i] 由小写字母组成

(2)解读与思路

没啥可说的,直接想法是双指针或者合并成一个string,不过如果word数组里的字母太多,后者似乎不太合算,考虑到提示里说了word1的总长度不超过1000,最终还是选择了后者,直接通过。

(3)代码实现

class Solution {
public:
    bool arrayStringsAreEqual(vector<string>& word1, vector<string>& word2) {
        string tmp1 = "";
        string tmp2 = "";
        for(int i = 0; i < word1.size(); i++) {
            tmp1 += word1[i];
        }
        for(int i = 0; i < word2.size(); i++) {
            tmp2 += word2[i];
        }
        return tmp1 == tmp2;
    }
};

2. 具有给定数值的最小字符串

(1)题目

小写字符 的 数值 是它在字母表中的位置(从 1 开始),因此 a 的数值为 1 ,b 的数值为 2 ,c 的数值为 3 ,以此类推。

字符串由若干小写字符组成,字符串的数值 为各字符的数值之和。例如,字符串 “abe” 的数值等于 1 + 2 + 5 = 8 。

给你两个整数 n 和 k 。返回 长度 等于 n 且 数值 等于 k 的 字典序最小 的字符串。

注意,如果字符串 x 在字典排序中位于 y 之前,就认为 x 字典序比 y 小,有以下两种情况:

x 是 y 的一个前缀;
如果 i 是 x[i] != y[i] 的第一个位置,且 x[i] 在字母表中的位置比 y[i] 靠前。

示例 1:
输入:n = 3, k = 27
输出:"aay"
解释:字符串的数值为 1 + 1 + 25 = 27,它是数值满足要求且长度等于 3 字典序最小的字符串。
示例 2:
输入:n = 5, k = 73
输出:"aaszz"
提示:
1 <= n <= 10^5
n <= k <= 26 * n

(2)解读与思路

一般看到字典序的题我是比较头疼的,不过这个还好,尽可能把小的字母(数)放在前面就好了,不过这个个数显然是受到限制的,从示例3就能看出来,我们首选自然是字母a,但是我们只能放两个。这里我们假设在n、k的条件下,我们首先可以放置t个字母i(数字i对应的字母),则

( n − t ) ∗ 26 > = k − i ∗ t (n - t) * 26 >= k - i * t (nt)26>=kit

最坏的情况即后面全部放置z,因此,t需要满足:

( 26 − i ) ∗ t < = 26 ∗ n − k (26 - i) * t <= 26 * n - k (26i)t<=26nk

因此只需要按照i从1到26的顺序考虑即可,对于i = 1的情况,首先放置最大可能个数的字母a,后面的问题就变成了对于如下参数的问题,并且i可以从2开始考虑了:

n = n − t ,   k = k − i ∗ t n = n - t , \ k = k - i * t n=nt, k=kit

简化一下,实际上只考虑每次余下的26 * n - k即可,每次的t由此确认。注意在到达z的时候公式的左边恒为0,只需要根据n的大小补0即可。

(3)代码实现

class Solution {
public:
    string getSmallestString(int n, int k) {
        int i = 1;
        string res = "";
        int r = 26 * n - k;
        while(i != 26) {
            int cnt = r / (26 - i);
            r = r % (26 - i);
            for(int j = 0; j < cnt; j++)
                res.push_back((char)('a' + i - 1));
            i++;
        }
        while(res.size() != n)
            res.push_back('z');
        return res;
    }
};

3. 生成平衡数组的方案数

(1)题目

给你一个整数数组 nums 。你需要选择 恰好 一个下标(下标从 0 开始)并删除对应的元素。请注意剩下元素的下标可能会因为删除操作而发生改变。

比方说,如果 nums = [6,1,7,4,1] ,那么:

选择删除下标 1 ,剩下的数组为 nums = [6,7,4,1] 。
选择删除下标 2 ,剩下的数组为 nums = [6,1,4,1] 。
选择删除下标 4 ,剩下的数组为 nums = [6,1,7,4] 。
如果一个数组满足奇数下标元素的和与偶数下标元素的和相等,该数组就是一个 平衡数组 。

请你返回删除操作后,剩下的数组 nums 是 平衡数组 的 方案数 。

示例 1:
输入:nums = [2,1,6,4]
输出:1
解释:
删除下标 0[1,6,4] -> 偶数元素下标为:1 + 4 = 5 。奇数元素下标为:6 。不平衡。
删除下标 1[2,6,4] -> 偶数元素下标为:2 + 4 = 6 。奇数元素下标为:6 。平衡。
删除下标 2[2,1,4] -> 偶数元素下标为:2 + 4 = 6 。奇数元素下标为:1 。不平衡。
删除下标 3[2,1,6] -> 偶数元素下标为:2 + 6 = 8 。奇数元素下标为:1 。不平衡。
只有一种让剩余数组成为平衡数组的方案。
示例 2:
输入:nums = [1,1,1]
输出:3
解释:你可以删除任意元素,剩余数组都是平衡数组。
示例 3:
输入:nums = [1,2,3]
输出:0
解释:不管删除哪个元素,剩下数组都不是平衡数组。
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^4

(2)解读与思路

这个题做的算是比较顺利了,下面的说明均使用原数组的下标。对于下标为k的数字,前面的所有偶数下标在该数删除后仍然是偶数下标,后面的所有偶数下标在该数删除后就变成了奇数下标;反之同理,前面的所有奇数下标在该数删除后仍然是奇数下标,后面的所有奇数下标在该数删除后就变成了偶数下标。这些都是显而易见的。
因此只需要对每个下标统计前面的奇数下标和、偶数下标和,后面的奇数下标和、偶数下标和,加一加是不是想等就好了。利用“前缀和”的思想,O(n)即可完成。
最后考虑一下提示,数字的和有可能比较大,没细考虑,保险起见就用了long long。

(3)代码实现

class Solution {
public:
    int waysToMakeFair(vector<int>& nums) {
        int n = nums.size();
        vector<long long> preodd(n, 0);
        vector<long long> preeven(n, 0);
        vector<long long> postodd(n, 0);
        vector<long long> posteven(n, 0);
        for(int i = 1; i < n; i++) {
            if(i % 2 == 1) {
                preeven[i] = preeven[i - 1] + nums[i - 1];
                preodd[i] = preodd[i - 1];
            }
            else {
                preeven[i] = preeven[i - 1];
                preodd[i] = preodd[i - 1] + nums[i - 1];
            }
        }
        for(int i = n - 2; i >= 0; i--) {
            if(i % 2 == 1) {
                posteven[i] = posteven[i + 1] + nums[i + 1];
                postodd[i] = postodd[i + 1];
            }
            else {
                posteven[i] = posteven[i + 1];
                postodd[i] = postodd[i + 1] + nums[i + 1];
            }
        }
        int res = 0;
        for(int i = 0; i < n; i++) {
            if(preodd[i] + posteven[i] == preeven[i] + postodd[i]) {
                res++;
            }
        }
        return res;
    }
};

4. 完成所有任务的最少初始能量

(1)题目

给你一个任务数组 tasks ,其中 tasks[i] = [actuali, minimumi] :

actuali 是完成第 i 个任务 需要耗费 的实际能量。
minimumi 是开始第 i 个任务前需要达到的最低能量。
比方说,如果任务为 [10, 12] 且你当前的能量为 11 ,那么你不能开始这个任务。如果你当前的能量为 13 ,你可以完成这个任务,且完成它后剩余能量为 3 。

你可以按照 任意顺序 完成任务。

请你返回完成所有任务的 最少 初始能量。

示例 1:
输入:tasks = [[1,2],[2,4],[4,8]]
输出:8
解释:
一开始有 8 能量,我们按照如下顺序完成任务:
    - 完成第 3 个任务,剩余能量为 8 - 4 = 4- 完成第 2 个任务,剩余能量为 4 - 2 = 2- 完成第 1 个任务,剩余能量为 2 - 1 = 1 。
注意到尽管我们有能量剩余,但是如果一开始只有 7 能量是不能完成所有任务的,因为我们无法开始第 3 个任务。
示例 2:
输入:tasks = [[1,3],[2,4],[10,11],[10,12],[8,9]]
输出:32
解释:
一开始有 32 能量,我们按照如下顺序完成任务:
    - 完成第 1 个任务,剩余能量为 32 - 1 = 31- 完成第 2 个任务,剩余能量为 31 - 2 = 29- 完成第 3 个任务,剩余能量为 29 - 10 = 19- 完成第 4 个任务,剩余能量为 19 - 10 = 9- 完成第 5 个任务,剩余能量为 9 - 8 = 1
示例 3:
输入:tasks = [[1,7],[2,8],[3,9],[4,10],[5,11],[6,12]]
输出:27
解释:
一开始有 27 能量,我们按照如下顺序完成任务:
    - 完成第 5 个任务,剩余能量为 27 - 5 = 22- 完成第 2 个任务,剩余能量为 22 - 2 = 20- 完成第 3 个任务,剩余能量为 20 - 3 = 17- 完成第 1 个任务,剩余能量为 17 - 1 = 16- 完成第 4 个任务,剩余能量为 16 - 4 = 12- 完成第 6 个任务,剩余能量为 12 - 6 = 6
提示:
1 <= tasks.length <= 10^5
1 <= actual​i <= minimumi <= 10^4

(2)解读与思路

首先不难想到结果有一个基本的下界,一方面能量不会低于所有任务所需的能量和,另一方面不会低于开始任意任务的最低能量。我的想法是从这一下界开始,模拟执行所有任务,过程中若存在不满足需求的情况,直接从此下界加上缺少的能量。
这个思路有点贪心的样子,关键在于如何找到最佳的任务执行顺序,实际上,通过示例2、3不难发现,按所需起始能量从小到大或从大到小执行都会有问题。原因在尝试的过程中逐渐显现出来,当我们执行了一个起始能量不高,但消耗能量比较多的任务时,再次执行某个起始能量较高的任务时,就容易使当前剩余的能量达不到该起始能量。
因此,执行的顺序即,首先执行起始能量比消耗能量大最多的任务,最后执行这两个能量相近的任务。执行的顺序由排序实现,sort中的cmp即比较执行能量与消耗能量差的绝对值,降序排序。
最后提交的时候有个小彩蛋,实际上,无论出于直觉还是逻辑,思路没啥问题,但是第一次TLE以及32/34,很尴尬,百度了一下sort怎么加速,实际上一开始我没有写参数引用,这点也并非大意,加引用一般来说会比较快这是显然的,只是我没想到会差这么多,在cmp中加上const和引用之后就通过了。
好吧,这五分钟算我白给了。

(3)代码实现

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        return abs(a[0] - a[1]) > abs(b[0] - b[1]);
    }
    
    int minimumEffort(vector<vector<int>>& tasks) {
        int res = 0, maxmin = 0;
        int n = tasks.size();
        for(int i = 0; i < n; i++) {
            res += tasks[i][0];
            maxmin = max(maxmin, tasks[i][1]);
        }
        res = max(res, maxmin);
        int init = res;
        sort(tasks.begin(), tasks.end(), cmp);
        for(int i = 0; i < n; i++) {
            if(init < tasks[i][1]) {
                res += tasks[i][1] - init;
                init = tasks[i][1];
            }
            init -= tasks[i][0];
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值