区间计数相关题目 907,828,795,3234,2444

907. Sum of Subarray Minimums
Medium

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; } }
复制代码

 

828. Count Unique Characters of All Substrings of a Given String
Hard

Let's define a function countUniqueChars(s) that returns the number of unique characters on s.

  • For example, calling countUniqueChars(s) if s = "LEETCODE" then "L""T""C""O""D" are the unique characters since they appear only once in s, therefore countUniqueChars(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

Medium

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.

ijs[i..j]Number of ZerosNumber 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.

ijs[i..j]Number of ZerosNumber 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;
    }
}
复制代码

 

posted @   xiaoyongyong  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示