区间计数相关题目 907,828,795,3234,2444
Given an array of integers arr, find the sum of min(b)
, where b
ranges over every (contiguous) subarray of arr
. Since the answer may be large, return the answer modulo 109 + 7
.
Example 1:
Input: arr = [3,1,2,4] Output: 17 Explanation: Subarrays are [3], [1], [2], [4], [3,1], [1,2], [2,4], [3,1,2], [1,2,4], [3,1,2,4]. Minimums are 3, 1, 2, 4, 1, 1, 2, 1, 1, 1. Sum is 17.
Example 2:
Input: arr = [11,81,94,43,3] Output: 444
Constraints:
1 <= arr.length <= 3 * 104
1 <= arr[i] <= 3 * 104
分析:
如果brute force的话,时间复杂度在O(N3) 或者 做优化后O(N2)
[3] [3,1] [3,1,2] [3,1,2,4]
[1] [1,2] [1,2,4]
[2] [2,4]
[4]
如果我们从每个数字角度考虑的话,会发现:
3可以贡献的只有一次[3]
1可以贡献的有 [3,1] [3,1,2] [3,1,2,4] [1] [1,2] [1,2,4]
2可以贡献的有 [2] [2,4]
4可以贡献的只有 [4]
因此,针对1 ,可以贡献的次数为:
[3,1,2,4] left=-1(左侧小于1的位置), right=4(右侧小于1的位置) ,pos=1(1自己所在位置), 1可以贡献的次数即为:(1-(-1))*(4-1) = 6
针对 2可以贡献的次数为:
[3,1,2,4] left=1, right = 4, 可以贡献的次数即为: (2-1)*(3-1) = 2
另外: 关于重复的情况:
[3,1,2,1]
3可以贡献的只有一次[3]
1可以贡献的有 [3,1] [1] [3,1,2] [1,2] [1,2,1] [3,1,2,1]
2可以贡献的有 [2] [2,4]
1可以贡献的只有 [3,1,2,1] [1,2,1] [2,1][1]
标红的部分会重复计算,我们应该在计算左侧最小时,包括相等的情况,右侧计算时不包含相等的情况
class Solution { public int sumSubarrayMins(int[] arr) {
//定义stack1计算每个数字的左侧/右侧小于自己的数字位置 Stack<Integer> stack1 = new Stack(); Stack<Integer> stack2 = new Stack(); int[] preSmaller = new int[arr.length]; int[] postSmaller = new int[arr.length]; for(int i=0;i<arr.length;i++){ while(!stack1.isEmpty() && arr[stack1.peek()] > arr[i]) //左侧计算包含相等的情况 stack1.pop(); preSmaller[i] = stack1.isEmpty() ? -1 : stack1.peek(); stack1.push(i); } for(int i=arr.length-1;i>=0;i--){ while(!stack2.isEmpty() && arr[stack2.peek()] >= arr[i]) // 右侧计算不包含相等的情况 stack2.pop(); postSmaller[i] = stack2.isEmpty() ? arr.length : stack2.peek(); stack2.push(i); }
//计算结束后 long sum = 0; long mod = (long)1e9 + 7; for(int i=0;i<arr.length;i++){ sum += ((long)(i-preSmaller[i]) * (postSmaller[i]-i) * arr[i])%mod; sum %= mod; } return (int)sum; } }
Let's define a function countUniqueChars(s)
that returns the number of unique characters on s
.
- For example, calling
countUniqueChars(s)
ifs = "LEETCODE"
then"L"
,"T"
,"C"
,"O"
,"D"
are the unique characters since they appear only once ins
, thereforecountUniqueChars(s) = 5
.
Given a string s
, return the sum of countUniqueChars(t)
where t
is a substring of s
. The test cases are generated such that the answer fits in a 32-bit integer.
Notice that some substrings can be repeated so in this case you have to count the repeated ones too.
Example 1:
Input: s = "ABC" Output: 10 Explanation: All possible substrings are: "A","B","C","AB","BC" and "ABC". Every substring is composed with only unique letters. Sum of lengths of all substring is 1 + 1 + 1 + 2 + 2 + 3 = 10
Example 2:
Input: s = "ABA"
Output: 8
Explanation: The same as example 1, except countUniqueChars
("ABA") = 1.
Example 3:
Input: s = "LEETCODE" Output: 92
Constraints:
1 <= s.length <= 105
s
consists of uppercase English letters only.
同样,bruteforce ,时间复杂度为O(N3) 或者 优化后O(N2)
但如果我们从每个字母的贡献度来考虑的话,比如:
ABCA
第一个A可以贡献的次数为: A,AB,ABC,ABCA
B可以贡献的次数为:AB,B,BC,BCA
class Solution { public int uniqueLetterString(String s) { int[] left = new int[s.length()]; int[] right = new int[s.length()]; int[] lastPos = new int[26]; Arrays.fill(lastPos, -1); for(int i=0;i<s.length();i++){ char c = s.charAt(i); left[i] = lastPos[c-'A']; lastPos[c-'A'] = i; } Arrays.fill(lastPos, s.length()); for(int i=s.length()-1;i>=0;i--){ char c = s.charAt(i); right[i] = lastPos[c-'A']; lastPos[c-'A'] = i; } int sum = 0; for(int i=0;i<s.length();i++){ sum += (i-left[i])*(right[i]-i); } return sum; } }
795. Number of Subarrays with Bounded Maximum
Given an integer array nums
and two integers left
and right
, return the number of contiguous non-empty subarrays such that the value of the maximum array element in that subarray is in the range [left, right]
.
The test cases are generated so that the answer will fit in a 32-bit integer.
Example 1:
Input: nums = [2,1,4,3], left = 2, right = 3 Output: 3 Explanation: There are three subarrays that meet the requirements: [2], [2, 1], [3].
Example 2:
Input: nums = [2,9,2,5,6], left = 2, right = 8 Output: 7
Constraints:
1 <= nums.length <= 105
0 <= nums[i] <= 109
0 <= left <= right <= 109
解法:
1. 计算每个元素可以作为最大元素的subarray的个数- 贡献次数
2. 过滤满足条件的元素(在left和right范围内的) 对贡献次数进行累加
跟907 非常类似,看每一个数字可以贡献的次数
3 pass
1st pass: 求出左侧最后一个大于等于该元素的位置 preGreaterEqual[]
2nd pass: 求出右侧第一个大于该元素的位置 postGreater[]
3rd pass: 根据左右侧个数
0 1 2 3 2, 1, 4, 3 pregreater:-1 0 -1 2 posgreater 2 2 4 4
注意点: 若干相同元素的时候,可能会重复计算,因此要使用preGreaterEqual, 而不是 preGreater
比如: 5,5,5 每个5都会左右边界算一次 ,就会重复计算
class Solution { public int numSubarrayBoundedMax(int[] nums, int left, int right) { int len = nums.length; //calculate the pre greater Stack<Integer> stack = new Stack(); int[] preGE = new int[len]; for(int i = 0; i < len; i++){ while(!stack.isEmpty() && nums[stack.peek()] <= nums[i]){ stack.pop(); } preGE[i] = stack.isEmpty() ? -1 : stack.peek(); stack.push(i); } //calculate the post greater stack.clear(); int[] postG = new int[len]; for(int i = len - 1; i >= 0; i--){ while(!stack.isEmpty() && nums[stack.peek()] < nums[i]){ stack.pop(); } postG[i] = stack.isEmpty() ? len : stack.peek(); stack.push(i); } //caluclate the result int result = 0; for(int i = 0; i < len; i++){ if(nums[i] >= left && nums[i] <= right) result += (i - preGE[i]) * (postG[i] - i); } return result; } }
You are given a binary string s
.
Return the number of substrings with dominant ones.
A string has dominant ones if the number of ones in the string is greater than or equal to the square of the number of zeros in the string.
Example 1:
Input: s = "00011"
Output: 5
Explanation:
The substrings with dominant ones are shown in the table below.
i | j | s[i..j] | Number of Zeros | Number of Ones |
---|---|---|---|---|
3 | 3 | 1 | 0 | 1 |
4 | 4 | 1 | 0 | 1 |
2 | 3 | 01 | 1 | 1 |
3 | 4 | 11 | 0 | 2 |
2 | 4 | 011 | 1 | 2 |
Example 2:
Input: s = "101101"
Output: 16
Explanation:
The substrings with non-dominant ones are shown in the table below.
Since there are 21 substrings total and 5 of them have non-dominant ones, it follows that there are 16 substrings with dominant ones.
i | j | s[i..j] | Number of Zeros | Number of Ones |
---|---|---|---|---|
1 | 1 | 0 | 1 | 0 |
4 | 4 | 0 | 1 | 0 |
1 | 4 | 0110 | 2 | 2 |
0 | 4 | 10110 | 2 | 3 |
1 | 5 | 01101 | 2 | 3 |
Constraints:
1 <= s.length <= 4 * 104
s
consists only of characters'0'
and'1'
.
class Solution { private Queue<Integer> que = new LinkedList<>(); private int len = 0; public int numberOfSubstrings(String s) { //1. 记录所有0的位置放入queue for(int i = 0; i < s.length(); i++) { if(s.charAt(i) == '0') que.offer(i); } len = s.length(); // 末尾也需要放入 que.offer(s.length()); //2. 从0~len-1作为起始位置开始统计 int result = 0; for(int i = 0; i < s.length(); i++) { // 已经在当前游标前的0不需要了,直接移除 while(que.peek() < i) que.poll(); // 计算当前游标向右的所有元素 result += count(que, i); } return result; } // 0 1 2 3 4 5 // 1 0 1 1 0 1| // ^ private int count(Queue<Integer> que, int start) { int result = 0; int zero = 0;//到目前0的个数 int lastCalPos = start;//上次计算位置 for(int pos : que) { // 关键点1: the number of ones in the string is greater than or equal to the square of the number of zeros if(zero * zero > len) break; // 满足valid的substring需要的最小end index int minInd = Math.max(start + zero + zero * zero - 1, lastCalPos); // 累加到result result += Math.max(0, pos - minInd); // 关键点2: 更新计算的最后位置 lastCalPos = pos; zero++; } return result; } }
You are given an integer array nums
and two integers minK
and maxK
.
A fixed-bound subarray of nums
is a subarray that satisfies the following conditions:
- The minimum value in the subarray is equal to
minK
. - The maximum value in the subarray is equal to
maxK
.
Return the number of fixed-bound subarrays.
A subarray is a contiguous part of an array.
Example 1:
Input: nums = [1,3,5,2,7,5], minK = 1, maxK = 5 Output: 2 Explanation: The fixed-bound subarrays are [1,3,5] and [1,3,5,2].
Example 2:
Input: nums = [1,1,1,1], minK = 1, maxK = 1 Output: 10 Explanation: Every subarray of nums is a fixed-bound subarray. There are 10 possible subarrays.
Constraints:
2 <= nums.length <= 105
1 <= nums[i], minK, maxK <= 106
class Solution { /** 思路: 记录minK和maxK和超出(minK,maxK)最后一次出现为位置 1. 如果minK,maxK都出现了,那么可以计算以当前元素为结尾的满足条件的个数:Math.min(preMin, preMax) - preBad, preBad 有可能出现在preMin, preMax之后 则不满足,所以Math.max(Math.min(preMin, preMax) - preBad, 0) */ public long countSubarrays(int[] nums, int minK, int maxK) { int preMin = -1;//记录min最后一次出现的位置 int preMax = -1;//记录max最后一次出现的位置 int preBad = -1;//记录超出范围的最后一次出现的位置 long result = 0; for(int i = 0; i < nums.length; i++) { if(nums[i] == minK){ //更新min的最后出现位置 preMin = i; } if(nums[i] == maxK){ //更新max的最后出现位置 preMax = i; } if(nums[i] > maxK || nums[i] < minK){ //更新bad的最后出现位置 preBad = i; } //如果截止当前位置,min和max都已经找到,那么可以计算出以当前i位置结尾能产生个数:Math.min(preMin, preMax) - preBad if(preMin >=0 && preMax >=0){ result += Math.max(Math.min(preMin, preMax) - preBad, 0); } } return result; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通