动态规划--Day05--最大子数组和--53. 最大子数组和,2606. 找到最大开销的子字符串,1749. 任意子数组和的绝对值的最大值
动态规划–Day05–最大子数组和–53. 最大子数组和,2606. 找到最大开销的子字符串,1749. 任意子数组和的绝对值的最大值
今天要训练的题目类型是:【最大子数组和】,题单来自@灵艾山茶府。
掌握动态规划(DP)是没有捷径的,咱们唯一能做的,就是投入时间猛猛刷题。
动态规划要至少刷100道才算入门!
53. 最大子数组和
思路【我】:
- 情况一:如果累加和已经负数了,再把它加到f[i],只会让f[i]更小。此时直接从f[i]开始累加。
- 情况二:else,直接累加
- f[i]只表示,这一段的最大子数组和。不是全局的最大子数组和。所以要用res记录全局最大值。
class Solution {public int maxSubArray(int[] nums) {int n = nums.length;int[] f = new int[n];// 初始化f[0] = nums[0];int res = nums[0];// 从1开始从前往后for (int i = 1; i < n; i++) {// 如果累加和已经负数了,再把它加到f[i],只会让f[i]更小// 此时直接从f[i]开始累加if (f[i - 1] < 0) {f[i] = nums[i];} else {// 累加f[i] = nums[i] + f[i - 1];}// 检查此时是否达到最大值res = Math.max(res, f[i]);}return res;}
}
思路【我】:
空间优化版本。发现当前i只跟i-1的状态有关。用一个变量即可。
class Solution {public int maxSubArray(int[] nums) {int n = nums.length;int res = nums[0];int cur = nums[0];for (int i = 1; i < n; i++) {if (cur < 0) {cur = nums[i];} else {cur += nums[i];}res = Math.max(res, cur);}return res;}
}
另一种写法:
class Solution {public int maxSubArray(int[] nums) {// 注意不能有非空数组,且有负数,不能初始化成 0int res = nums[0];int f = 0;for (int x : nums) {f = Math.max(f, 0) + x;res = Math.max(res, f);}return res;}
}
2606. 找到最大开销的子字符串
思路【我】:
根据题意,chars和vals是制定了一些元素的值,其余字母还有默认值。所以,先新建数组myVals,先填默认值,再填指定值。
其余步骤和上题53. 最大子数组和是一样的。
最后返回的时候注意,如果res小于0,要考虑空串的情况,空串的开销是0,返回0。
class Solution {public int maximumCostSubstring(String s, String chars, int[] vals) {char[] chars2 = chars.toCharArray();int[] myVals = new int[128];// 先填默认值for (int i = 'a'; i <= 'z'; i++) {myVals[i] = i + 1 - 'a';}// 再填指定值for (int i = 0; i < chars2.length; i++) {myVals[chars2[i]] = vals[i];}// 53. 最大子数组和char[] ch = s.toCharArray();int n = ch.length;int cur = myVals[ch[0]];int res = cur;for (int i = 1; i < n; i++) {if (cur < 0) {cur = myVals[ch[i]];} else {cur += myVals[ch[i]];}res = Math.max(res, cur);}// 如果res小于0, 那就返回空串的开销(0)return res < 0 ? 0 : res;}
}
解决 res=0 的另一种写法:
char[] ch = s.toCharArray();
int n = ch.length;
int cur = 0;
// 允许空串,即允许子数组为空,所以res初始化为0
int res = 0;
// 要从0开始遍历
for (int i = 0; i < n; i++) {if (cur < 0) {cur = myVals[ch[i]];} else {cur += myVals[ch[i]];}res = Math.max(res, cur);
}
return res;
1749. 任意子数组和的绝对值的最大值
思路【我】:
总思路:正着来一遍,负着来一遍。
求最大的正的子数组和,最小的负的子数组和。
看他俩的绝对值谁更大
class Solution {public int maxAbsoluteSum(int[] nums) {// 总思路:正着来一遍,负着来一遍int n = nums.length;// 初始化int pos = nums[0];int neg = nums[0];int resP = nums[0];int resN = nums[0];// 从1开始从前往后for (int i = 1; i < n; i++) {// 最大的正的子数组和if (pos < 0) {pos = nums[i];} else {pos += nums[i];}resP = Math.max(resP, pos);// 最小的负的子数组和if (neg > 0) {neg = nums[i];} else {neg += nums[i];}resN = Math.min(resN, neg);}// 看他俩的绝对值谁更大return Math.max(resP, Math.abs(resN));}
}
思路:
使用更少变量的写法:
class Solution {public int maxAbsoluteSum(int[] nums) {// 因为可以允许为空,所以初始值可以赋值为0,然后遍历整个数组。int res = 0;int pos = 0;int neg = 0;for (int x : nums) {// 求最大子数组和pos = Math.max(pos, 0) + x;// 求最小子数组和neg = Math.min(neg, 0) + x;// 每一步都要更新res。看看是正的更正,还是负的更负res = Math.max(res, Math.max(pos, -neg));}return res;}
}