1

For the question below: Given an array A of N non-negative numbers and a non-negative number B,you need to find the number of subarrays in A with a sum less than B.

I have found 2 solutions: Brute force:

let count = 0;
for (let i=0; i<A.length; i++) {
    let sum = BigInt(0);
    for (let j=i; j<A.length; j++) {
        sum = sum + BigInt(A[j]);
        if (sum < B) {
            count+=1;
        }
    }
}

return count;

Optimised solution:

const countSubarrays = (A, B) => {

    let start = 0;
    let end = 0;
    let ans = 0;
    let currentSum = 0;
    let n = A.length;

    while (end < n) {
        currentSum += A[end];
        if (currentSum >= B) {
            currentSum -= A[start];
            start++;
        }

        ans += (end - start) + 1;
        end++;
    }

    return ans;
}

const A = [2, 5, 6];
const B = 10;

const result = countSubarrays(A, B);
console.log('result: ', result);

But the result for optimised one is wrong. The formula to calculate all subarrays seems incorrect: ans += (end - start) + 1; Could anyone please let me know the correct approach.

thanks

Ashy Ashcsi
  • 1,529
  • 7
  • 22
  • 54
  • 1
    each time you add a new value A[end] to the currentSum, if the new currentSum >= B, you need to put the advancing start index in a while loop as well, i.e, keep incrementing start until currentSum < B. A[end] can be quite large and the extreme case is that A[end] itself is >=B, in this case you need to advance start all the way to end + 1. – Lyn Apr 12 '23 at 17:29
  • This seems like a combinatorics problem. Why not use `recursion`? It will be neat and straightforward – Onyambu Apr 12 '23 at 19:00
  • I dont know which language you are coding in, but this can easily be thought of as deleting each element from the array while checking as to whether its is less than the target and at the same time reducing the target value by the deleted element. eg let function name be fun(vec, target);`if(length of vec is zero and target is not the initialized value) return target > 0 ; otherwise return fun(vector with the first element removed, target) + fun(vector with first element removed, target - the removed element from vector)` – Onyambu Apr 12 '23 at 19:28

2 Answers2

1

The answer provided by @onyambu solves a wrong problem. The brute force solution clearly indicates that we are computing the number of subarrays, not subsequences. A subarray is continuous. For example, [2,5] is a subarray of [2,5,6] but [2,6] is not.

The problem in your optimized solution is exactly what I described in the comment. You need to keep subtracting A[start] as long as currentSum >= B before you update your answer.

The following Java code explains the correct approach with some comments.

class Solution {
    public long countSmallerSumSubarrays(int[] A, long B) {
        //if B is 0, since A[i] >= 0, no subarray is going to have a sum < 0
        if(B == 0) {
            return 0;
        }
        long ans = 0, runningSum = 0;
        int left = 0, right = 0;
        //key idea: try to compute the number of subarrays that end at A[right] with sum < B.
        //The final answer is the sum over all possible ending indices from 0 to A.length - 1.
        while(right < A.length) {
            //add the current number into running sum
            runningSum += A[right];
            //if running sum < B, it means all subarrays with left bound index in [left, right] and right bound index as right
            //are valid
            if(runningSum < B) {
                ans += right - left + 1;
            }
            //otherwise, we need to move the left index till the running sum < B
            else {
                while(runningSum >= B) {
                    runningSum -= A[left];
                    left++;
                }
                ans += right - left + 1;
            }
            //we are done computing the number of subarrays that end at A[right] with sum < B, advance right index to the next one
            right++;
        }
        return ans;
    }
    public static void main(String[] args) {
        int[] A = new int[]{2,5,6};
        Solution solution = new Solution();
        System.out.println(solution.countSmallerSumSubarrays(A, 10));
    }
}
Lyn
  • 637
  • 5
  • 16
  • while this approach is correct, answer is always one greater than actual result. Please find the test cases below: const A = [1, 11, 2, 3, 15]; const B = 10; ans is 5, actual answer should be 4 – Ashy Ashcsi Apr 17 '23 at 04:53
  • Did you run the code? It outputs 4, using your test case. The reason that this is correct is that when a single number A[i] >= B, left will become right + 1, hence right - left + 1 is 0. – Lyn Apr 17 '23 at 17:17
0

This can be accomplished using polynomial time:

In JS:

function count(vec, target){
  let cnt = 0;
  while(vec.length){
    let sm = 0;
    for (let i = 0; i < vec.length & target >= sm + vec[i];  i++,cnt++)
      sm += vec[i];
    vec.splice(0,1);
  }
  return cnt;
}
console.log(count([2,5,6], 10))
console.log(count([1,2,3,4,5,6,7,8],10))

C/C++:

#include <stdio.h>

int count (int * vec, int target, int m){
  int cnt = 0;
  while(m){
    int sum = 0;
    for (int i = 0; i < m & target >= sum + vec[i];  i++,cnt++)
      sum += vec[i];
    vec++;
    m--;
  }
  return cnt;
}

int main() {
    int v[3] = {2,5,6};
    int v2[8]= {1,2,3,4,5,6,7,8};
    printf("count: %d\n", count(v, 10, 3));
    printf("count: %d\n", count(v2, 10, 8));
    return 0;
}

count: 4
count: 15

Python:

def count(vec, target):
  cnt = 0
  while vec:
    sm, i = 0, 0
    while i < len(vec) and target >= (sm +vec[i]):
        sm += vec[i]
        cnt += 1
        #print(i,"\t", cnt, "\t", sm, "\t", vec[:i+1])
        i += 1
    vec = vec[1:]
  return cnt
print(count([2,5,6],10))
print(count([1,2,3,4,5,6,7,8],10))
4
15
Onyambu
  • 67,392
  • 3
  • 24
  • 53