2104. 子数组范围和
2022.3.4 每日一题
题目描述
给你一个整数数组 nums 。nums 中,子数组的 范围 是子数组中最大元素和最小元素的差值。
返回 nums 中 所有 子数组范围的 和 。
子数组是数组中一个连续 非空 的元素序列。
示例 1:
输入:nums = [1,2,3]
输出:4
解释:nums 的 6 个子数组如下所示:
[1],范围 = 最大 - 最小 = 1 - 1 = 0
[2],范围 = 2 - 2 = 0
[3],范围 = 3 - 3 = 0
[1,2],范围 = 2 - 1 = 1
[2,3],范围 = 3 - 2 = 1
[1,2,3],范围 = 3 - 1 = 2
所有范围的和是 0 + 0 + 0 + 1 + 1 + 2 = 4
示例 2:
输入:nums = [1,3,3]
输出:4
解释:nums 的 6 个子数组如下所示:
[1],范围 = 最大 - 最小 = 1 - 1 = 0
[3],范围 = 3 - 3 = 0
[3],范围 = 3 - 3 = 0
[1,3],范围 = 3 - 1 = 2
[3,3],范围 = 3 - 3 = 0
[1,3,3],范围 = 3 - 1 = 2
所有范围的和是 0 + 0 + 0 + 2 + 0 + 2 = 4
示例 3:
输入:nums = [4,-2,-3,4,1]
输出:59
解释:nums 中所有子数组范围的和是 59
提示:
1 <= nums.length <= 1000
-10^9 <= nums[i] <= 10^9
进阶:你可以设计一种时间复杂度为 O(n) 的解决方案吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-of-subarray-ranges
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
class Solution {
public long subArrayRanges(int[] nums) {
//看到数据范围,能想到的最直接的方法就是两层循环找每个子数组的最大最小值
//先写一下过了
int n = nums.length;
long res = 0;
for(int i = 0; i < n; i++){
int max = nums[i];
int min = nums[i];
for(int j = i; j < n; j++){
max = Math.max(max, nums[j]);
min = Math.min(min, nums[j]);
res += max - min;
}
}
return res;
}
}
转变一下思维,不是去一个一个求每个子数组的最大最小
而是求所有子数组的最小值之和,所有子数组的最大值之和,然后相减
而在求最小值的时候,如果nums[i]在一个范围内是最小呢,那么在这个范围内包含nums[i]的所有子数组最小值都是num[i],可以根据这个思想找到这个范围
创建左右两边的单调栈,分别处理小于和大于i位置的最小值,就找到了这个范围
但是需要特别注意的是,需要规定一个逻辑大小
也就是说需要规定如果数组中两个数是相同呢,那么下标小的数逻辑上是小的
这样规定以后,所有处理都遵循这个规则,就不会出错
class Solution {
public long subArrayRanges(int[] nums) {
//看到数据范围,能想到的最直接的方法就是两层循环找每个子数组的最大最小值
//先写一下过了
//接下来就是On的复杂度
//空间换时间,用优先队列行不行,不太行
//先用一个数组记录下nums[0]组成的所有子数组的范围
//然后开始遍历整个数组,如果这个值改变了
//不会,看题解了
//首先要想做成On的复杂度,必须先想到 所有子数组最大值和最小值的差
//就等于所有子数组的最大值减去所有子数组的最小值
//那么接下来的问题就不是求每个子数组的对应的最大值最小值了,
//而可以求所有子数组的最小值,然后再求一遍最大值
//那么,怎么求所有子数组的最小值呢
//对于nums[i],左边第一个比它小的下标是left,右边第一个比它小的下标是right
//那么在left到right范围内的所有子数组的最小值都是nums[i],也就是(i - left) * (right - i)
//然后现在的问题就是找这两个left 和right了
//这里用的是单调栈,用两个单调栈分别处理两边
int n = nums.length;
int[] minleft = new int[n];
int[] minright = new int[n];
int[] maxleft = new int[n];
int[] maxright = new int[n];
Stack<Integer> minStack = new Stack<Integer>();
Stack<Integer> maxStack = new Stack<Integer>();
//处理左边
for(int i = 0; i < n; i++){
//如果栈顶元素大于当前元素,弹出
while(!minStack.isEmpty() && nums[minStack.peek()] > nums[i]){
minStack.pop();
}
//如果栈为空的话,说明左边没有比它小的元素,所以赋值为-1
if(minStack.isEmpty()){
minleft[i] = -1;
}else{
minleft[i] = minStack.peek();
}
minStack.push(i);
//这里为什么要加等于,因为上面在处理最小值的时候没有加等于,默认的比较规则是如果数值相等
//那么如果位置在前,那么就小,
//如果用这个规则统一处理的话,那么这里位置在前同样是小数,所以是小于等于
while(!maxStack.isEmpty() && nums[maxStack.peek()] <= nums[i]){
maxStack.pop();
}
if(maxStack.isEmpty()){
maxleft[i] = -1;
}else{
maxleft[i] = maxStack.peek();
}
maxStack.push(i);
}
minStack.clear();
maxStack.clear();
//处理右边
for(int i = n - 1; i >= 0; i--){
//如果栈顶元素大于当前元素,弹出
while(!minStack.isEmpty() && nums[minStack.peek()] >= nums[i]){
minStack.pop();
}
//如果栈为空的话,说明左边没有比它小的元素,所以赋值为-1
//为了后面方便计算,这里赋值为n
if(minStack.isEmpty()){
minright[i] = n;
}else{
minright[i] = minStack.peek();
}
minStack.push(i);
while(!maxStack.isEmpty() && nums[maxStack.peek()] < nums[i]){
maxStack.pop();
}
if(maxStack.isEmpty()){
maxright[i] = n;
}else{
maxright[i] = maxStack.peek();
}
maxStack.push(i);
}
long sumMax = 0;
long sumMin = 0;
for(int i = 0; i < n; i++){
sumMax += nums[i] * (long)(i - maxleft[i]) * (maxright[i] - i);
sumMin += nums[i] * (long)(i - minleft[i]) * (minright[i] - i);
}
return sumMax - sumMin;
}
}
521. 最长特殊序列 Ⅰ
2022.3.5 每日一题
题目描述
给你两个字符串 a 和 b,请返回 这两个字符串中 最长的特殊序列 。如果不存在,则返回 -1 。
「最长特殊序列」 定义如下:该序列为 某字符串独有的最长子序列(即不能是其他字符串的子序列) 。
字符串 s 的子序列是在从 s 中删除任意数量的字符后可以获得的字符串。
例如,“abc” 是 “aebdc” 的子序列,因为删除 “aebdc” 中斜体加粗的字符得到 “abc” 。 “aebdc” 的子序列还包括 “aebdc” 、 “aeb” 和 “” (空字符串)。
示例 1:
输入: a = “aba”, b = “cdc”
输出: 3
解释: 最长特殊序列可为 “aba” (或 “cdc”),两者均为自身的子序列且不是对方的子序列。
示例 2:
输入:a = “aaa”, b = “bbb”
输出:3
解释: 最长特殊序列是 “aaa” 和 “bbb” 。
示例 3:
输入:a = “aaa”, b = “aaa”
输出:-1
解释: 字符串 a 的每个子序列也是字符串 b 的每个子序列。同样,字符串 b 的每个子序列也是字符串 a 的子序列。
提示:
1 <= a.length, b.length <= 100
a 和 b 由小写英文字母组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-uncommon-subsequence-i
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
class Solution {
public int findLUSlength(String a, String b) {
//这个题是什么意思呢,就是在a中找到b中没有的最长子序列
//看起来好像很复杂
//其实想想,如果两个字符串相同,那么是-1
//如果两个字符串不相同,那么长度大于等于的肯定不会出现在另一个中
//所以直接返回长度长的就行了
int la = a.length();
int lb = b.length();
if(la > lb)
return la;
else if(lb > la)
return lb;
for(int i = 0; i < la; i++){
if(a.charAt(i) != b.charAt(i))
return la;
}
return -1;
}
}
2100. 适合打劫银行的日子
2022.3.6 每日一题
题目描述
你和一群强盗准备打劫银行。给你一个下标从 0 开始的整数数组 security ,其中 security[i] 是第 i 天执勤警卫的数量。日子从 0 开始编号。同时给你一个整数 time 。
如果第 i 天满足以下所有条件,我们称它为一个适合打劫银行的日子:
第 i 天前和后都分别至少有 time 天。
第 i 天前连续 time 天警卫数目都是非递增的。
第 i 天后连续 time 天警卫数目都是非递减的。
更正式的,第 i 天是一个合适打劫银行的日子当且仅当:security[i - time] >= security[i - time + 1] >= … >= security[i] <= … <= security[i + time - 1] <= security[i + time].
请你返回一个数组,包含 所有 适合打劫银行的日子(下标从 0 开始)。返回的日子可以 任意 顺序排列。
示例 1:
输入:security = [5,3,3,3,5,6,2], time = 2
输出:[2,3]
解释:
第 2 天,我们有 security[0] >= security[1] >= security[2] <= security[3] <= security[4] 。
第 3 天,我们有 security[1] >= security[2] >= security[3] <= security[4] <= security[5] 。
没有其他日子符合这个条件,所以日子 2 和 3 是适合打劫银行的日子。
示例 2:
输入:security = [1,1,1,1,1], time = 0
输出:[0,1,2,3,4]
解释:
因为 time 等于 0 ,所以每一天都是适合打劫银行的日子,所以返回每一天。
示例 3:
输入:security = [1,2,3,4,5,6], time = 2
输出:[]
解释:
没有任何一天的前 2 天警卫数目是非递增的。
所以没有适合打劫银行的日子,返回空数组。
示例 4:
输入:security = [1], time = 5
输出:[]
解释:
没有日子前面和后面有 5 天时间。
所以没有适合打劫银行的日子,返回空数组。
提示:
1 <= security.length <= 10^5
0 <= security[i], time <= 10^5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-good-days-to-rob-the-bank
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
左右遍历两次,把满足条件的找出来
class Solution {
public List<Integer> goodDaysToRobBank(int[] security, int time) {
//从左到右先找递减的,然后找递增的
int n = security.length;
Set<Integer> jian = new HashSet<>();
int temp = 100001;
int count = -1;
for(int i = 0; i < n; i++){
if(security[i] <= temp){
count++;
}else{
count = 0;
}
if(count >= time){
jian.add(i);
}
temp = security[i];
}
Set<Integer> jia = new HashSet<>();
temp = 100001;
count = -1;
for(int i = n - 1; i >= 0; i--){
if(security[i] <= temp){
count++;
}else{
count = 0;
}
if(count >= time){
jia.add(i);
}
temp = security[i];
}
List<Integer> res = new ArrayList<>();
for(int t : jian){
if(jia.contains(t))
res.add(t);
}
return res;
}
}