目录
- 978. 最长湍流子数组
- 题目
- 思路分析以及代码
- 1052. 爱生气的书店老板
- 题目
- 思考分析与初步代码
- 优化思路以及优化代码
- 1208. 尽可能使字符串相等
- 题目
- 思考分析以及代码
978. 最长湍流子数组
https://leetcode-cn.com/problems/longest-turbulent-subarray/
题目
当 A 的子数组 A[i], A[i+1], …, A[j] 满足下列条件时,我们称其为湍流子数组:
若 i <= k < j,当 k 为奇数时, A[k] > A[k+1],且当 k 为偶数时,A[k] < A[k+1];
或 若 i <= k < j,当 k 为偶数时,A[k] > A[k+1] ,且当 k 为奇数时, A[k] < A[k+1]。
也就是说,如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是湍流子数组。
返回 A 的最大湍流子数组的长度。
示例1:
输入:[9,4,2,10,7,8,8,1,9]
输出:5
解释:(A[1] > A[2] < A[3] > A[4] < A[5])
示例2:
输入:[4,8,12,16]
输出:2
示例 3:
输入:[100]
输出:1
提示:
1 <= A.length <= 40000
0 <= A[i] <= 10^9
思路分析以及代码
这一题是典型的滑动窗口问题。
我的大致思路如下:
1、获取差分数组
2、从左到右比对差分数组,观察相邻两个元素是否异号(一个大于0 ,一个小于0;或者相反)
3、如果异号,说明符合条件;更新窗口值,右边界继续扩展
4、否则,说明当前[left,right)中都不符合条件,需要直接将左边界更新到原有的右边界。
5、意外情况:下面有几种意外情况我是特殊处理的
1、原数组小于等于1,直接返回数组大小
2、如果差分数组中全部都是0,此时返回1
class Solution {
public:int juge(int a , int b){if((a > 0 && b < 0) || (a < 0 && b >0)) return true;else return false;}int maxTurbulenceSize(vector<int>& arr) {if(arr.size() <= 1) return arr.size();int n = arr.size();vector<int> diff(n - 1);for(int i = 0; i < n - 1; i++){diff[i] = arr[i+1] - arr[i];}int left = 0 ,right = 1;int max_window_size = 1;int zero_diff_times = 0;if(diff[0] == 0) zero_diff_times++;while(right < diff.size()){if(juge(diff[right],diff[right - 1])){max_window_size = max(max_window_size,right - left + 1);}else{if(diff[right] == 0) zero_diff_times++;left = right;}right++;}if(zero_diff_times == diff.size()) return 1;return max_window_size+1;}
};
1052. 爱生气的书店老板
https://leetcode-cn.com/problems/grumpy-bookstore-owner/
题目
今天,书店老板有一家店打算试营业 customers.length 分钟。每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开。
在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气,那么 grumpy[i] = 1,否则 grumpy[i] = 0。 当书店老板生气时,那一分钟的顾客就会不满意,不生气则他们是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 X 分钟不生气,但却只能使用一次。
请你返回这一天营业下来,最多有多少客户能够感到满意的数量。
示例:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], X = 3
输出:16
解释:
书店老板在最后 3 分钟保持冷静。
感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.
提示:
1 <= X <= customers.length == grumpy.length <= 20000
0 <= customers[i] <= 1000
0 <= grumpy[i] <= 1
思考分析与初步代码
初步思路:
1、从第一个时间段开始遍历,遍历每个窗口长度为X的时间段,并且计算这个时间段内老板生气的时间顾客来的人数。
2、依次遍历完整个时间段,找到时间段内老板生气的时间顾客来的人数最多的人数。
3、最后再遍历一遍原数组,记录下老板不生气时候来的总人数
4、两个结果相加,得到最终结果。
class Solution {
public:int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int X) {int max_sum_start = 0;int max_sum = 0;for(int start = 0; start < customers.size() - X + 1; start++){int sum = 0;for(int i = start; i < start + X ; i ++){if(grumpy[i] == 1) sum += customers[i];}if(sum >= max_sum){max_sum = sum;max_sum_start = start;}}//cout << "max_sum= " << max_sum << endl;int result = 0;for(int i = 0; i < customers.size(); i++){if(grumpy[i] == 0) result+=customers[i];}return result+max_sum;}
};
很显然时间复杂度是O(N * X)
优化思路以及优化代码
关于长度固定的滑动窗口,有一点我们要熟悉的是,窗口每次滑动时,变化的只有头尾两个元素,所以我们只针对这两个元素进行处理就行。
不过对于第一个窗口需要特殊对待,需要先进行填充。
接下来的窗口中,如果新进来的时间是生气的,加上顾客量。如果新出去的时间是生气的,那么减去顾客量。每次滑动都需要更新最大值。
class Solution {
public:int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int X) {int sum = 0;//计算不生气时来的顾客总数for(int i = 0; i < customers.size(); i++){if(grumpy[i] == 0) sum+=customers[i];}int left = 0, right = 0;//第一个区域while(right < X){if(grumpy[right] == 1) sum += customers[right];right++;}int max_sum = sum;while(right < customers.size()){//生气的时间段被移除窗口,总顾客减少if(grumpy[left] == 1) sum -= customers[left];//生气的时间段被移入窗口,总顾客增加if(grumpy[right] == 1) sum += customers[right];max_sum = max(max_sum,sum);left++;right++;}return max_sum;}
};
1208. 尽可能使字符串相等
题目
给你两个长度相同的字符串,s 和 t。
将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。
用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。
如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。
如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0。
示例 1:
输入:s = “abcd”, t = “bcdf”, cost = 3
输出:3
解释:s 中的 “abc” 可以变为 “bcd”。开销为 3,所以最大长度为 3。
示例 2:
输入:s = “abcd”, t = “cdef”, cost = 3
输出:1
解释:s 中的任一字符要想变成 t 中对应的字符,其开销都是 2。因此,最大长度为 1。
示例 3:
输入:s = “abcd”, t = “acde”, cost = 0
输出:1
解释:你无法作出任何改动,所以最大长度为 1。
提示:
1 <= s.length, t.length <= 10^5
0 <= maxCost <= 10^6
s 和 t 都只含小写英文字母。
思考分析以及代码
这一题是一个非常典型的滑动窗口题。
1、首先我们构建差分绝对值数组:
vector<int> diff(s.size());
for(int i = 0; i < diff.size(); i++)
{diff[i] = abs(s[i] - t[i]);
}
2、然后,我们建立滑动窗口的左右边界,在差分数组上移动
3、什么时候扩展窗口?
当我们现在存有的cost - 右边新纳入的cost >= 0 的时候,我们可以继续扩展窗口
4、反之,如果小于0,说明不能继续扩展了。但是由于我们求的是最大窗口长度,所以此时也不需要缩小窗口,只需要将整个窗口平移即可。记住平移的时候,要将新移除的cost加到当前cost上,算是弥补损失。
5、更新窗口最大长度的操作必然是在扩展窗口的时候啦
这一题思路应该还是比较清晰的。当然也有个地方有点不好搞,那就是第三点是>0 还是 >=0.如果有时间的话,可以自己手动推导一下,如果没有就两个都试一下,选择答案正确的即可.
class Solution {
public:int equalSubstring(string s, string t, int maxCost) {//构建相差数组vector<int> diff(s.size());for(int i = 0; i < diff.size(); i++){diff[i] = abs(s[i] - t[i]);}int left = 0, right = 0;int max_length = 0;while(right < diff.size()){//扩大窗口int new_In = diff[right];maxCost = maxCost - new_In;if(maxCost >= 0){max_length = max(max_length,right -left + 1);}else{int new_Out = diff[left];maxCost += new_Out;left++;}right++;}return max_length;}
};