算法习题-力扣446周赛题解
算法可以调度思维,让程序员的思维发散,找到更好的解决方案。
第一题:执行指令后的得分
题目:
给你两个数组:
instructions
和values
,数组的长度均为n
。你需要根据以下规则模拟一个过程:
从下标
i = 0
的第一个指令开始,初始得分为 0。
- 如果
instructions[i]
是add
:将values[i]
加到你的得分中。移动到下一个指令(i + 1)
- 如果
instructions[i]
是jump
:移动到下标为(i + values[i])
的指令,但不修改你的得分。当以下任一情况发生时,过程会终止:
越界(即i < 0
或i >= n
),或尝试再次执行已经执行过的指令。被重复访问的指令不会再次执行。返回过程结束时的得分。
思路:
遍历一遍操作数,如果如果遇到的是
add
,就将得分加到自己的得分中。如果遇到的是jump
,那末就将循环变量自增value-1
,减 1 是因为下一次循环会增 1 ,刚好抵消。
代码实现
class Solution {public long calculateScore(String[] instructions, int[] values) {long ans = 0;for(int i = 0;i >= 0 && i < instructions.length;i++) {if("ok".equals(instructions[i])) break;String op = instructions[i];instructions[i] = "ok";if("add".equals(op)) {ans += values[i];}if("jump".equals(op)) {i += values[i]-1;}}return ans;}
}
第二题:非递减数组的最大长度
题目:
给你一个整数数组
nums
。在一次操作中,你可以选择一个子数组,并将其替换为一个等于该子数组 最大值 的单个元素。返回经过零次或多次操作后,数组仍为 非递减 的情况下,数组 可能的最大长度。子数组 是数组中一个连续、非空 的元素序列。
思路:
根据题意,假设遇到数组中的第
i
项为 a i a_i ai 那末 a i a_i ai 后面所有比它小的项都会被替换为 a i a_i ai ,直到遇到下一个比它大的项,就会开启下一轮的新的替换。比如:4 1 2 3 5 1 3,那末,第
1
项后所有比4
小的数,都会被替换为4
,直到遇到5
才会开启下一轮新的替换,因此,样例的答案为:2 (最后的数组为:[4,5]
)故,只需要记录最大的前一项即可。
代码实现
class Solution {public int maximumPossibleSize(int[] nums) {int ans = 1,t = nums[0];for(int i = 1;i < nums.length;i++)if(t <= nums[i]) {ans ++;t = nums[i];}return ans;}
}
第三题:求出数组的X值 I
题目:
给你一个由 正 整数组成的数组
nums
,以及一个 正 整数k
。你可以对nums
执行 一次 操作,该操作中可以移除任意 不重叠 的前缀和后缀,使得nums
仍然 非空 。你需要找出nums
的 x 值,即在执行操作后,剩余元素的 乘积 除以k
后的 余数 为x
的操作数量。返回一个大小为k
的数组result
,其中result[x]
表示对于0 <= x <= k - 1
,nums
的 x 值。数组的 前缀 指从数组起始位置开始到数组中任意位置的一段连续子数组。数组的 后缀 是指从数组中任意位置开始到数组末尾的一段连续子数组。子数组 是数组中一段连续的元素序列。注意,在操作中选择的前缀和后缀可以是 空的 。
思路:
题目的意思其实就是找到一个子数组,使得子数组的乘积模
k
的每种情况的数量。可以设置f[i ,j]
表示以第i
项结尾的所有模k
结果为j
的所有子数组。因此,考虑f[i-1 , j]
项,设f[i,p]
项是f[i-1, j]
项转移而来的,因此j
是前i-1
项模k
的结果,故,当包含第i
项时,
j = ■ % k,此时,算上最后一项,p = (nums_i * ■) % k = (nums_i % k * ■ % k) % k = j * (nums_i % k) % k
因此,转移方程为:
f[i,j * (nums_i % k) % k] += f[i-1, j]
最后,考虑刚开始时,
f[i,nums_i % k] = 1
必定有一个。
代码实现
public class Solution {public long[] resultArray(int[] nums, int k) {long[] ans = new long[k];int[][] f = new int[nums.length+1][k];for(int i = 1;i < f.length;i++) {f[i][nums[i-1] % k] = 1;for(int j = 0;j < k;j++) {f[i][j * (nums[i-1] % k) % k] += f[i-1][j];}}// 最后只需要统计一遍即可for(int i = 1;i < f.length;i++) {for(int j = 0;j < k;j++) {ans[j] += f[i][j];}}return ans;}
}
第四题:求出数组的X值 II
这题对于我现有的情况来说,已经很难了,因此,只能通过80% ~ 90%,搜到的题解有些难以理解,等我后期再理解理解
题目:
给你一个由 正 整数组成的数组
nums
,以及一个 正 整数k
。你可以对nums
执行 一次 操作,该操作中可以移除任意 不重叠 的前缀和后缀,使得nums
仍然 非空 。你需要找出nums
的 x 值,即在执行操作后,剩余元素的 乘积 除以k
后的 余数 为x
的操作数量。返回一个大小为k
的数组result
,其中result[x]
表示对于0 <= x <= k - 1
,nums
的 x 值。数组的 前缀 指从数组起始位置开始到数组中任意位置的一段连续子数组。数组的 后缀 是指从数组中任意位置开始到数组末尾的一段连续子数组。子数组 是数组中一段连续的元素序列。注意,在操作中选择的前缀和后缀可以是 空的 。
思路:
题目的意思实际就是,每次给一个下标(
index
) 和 替换的值(value
),每次将数组中的下标为index
的位置替换为value
,之后,给定一个起始下标(start
) 和 值(x
),找到从start
开始,直到数组的末尾的所有前缀的乘积模k
的值为x
的前缀的个数,因此我直接通过暴力解法没有全对。
算法实现
class Solution {public int[] resultArray(int[] nums, int k, int[][] queries) {int[] ans = new int[queries.length];for(int i = 0;i < queries.length;i++) {int index = queries[i][0];int value = queries[i][1];int start = queries[i][2];int x = queries[i][3];nums[index] = value;long res = 1;int ans_t = 0;for(int j = start;j < nums.length;j++) {res = res * nums[j] % k;if(res % k == x) ans_t ++;}ans[i] = ans_t;}return ans;}
}