6

Given an array of 'n' integers, i need to find for each element of the array, the number of continuous subarrays that have that element as its max element.

Elements can repeat.

Is there a way to do it in less than O(n^2).

O(nlogn) or O(n)?

Example-
If array is {1,2,3}. Then-
For '1': 1 Such subarray {1}.
For '2': 2 Such subarrays {2},{1,2}
For '3': 3 Such subarrays {3},{2,3},{1,2,3}
hasan
  • 23,815
  • 10
  • 63
  • 101
LTim
  • 473
  • 1
  • 4
  • 15
  • Is this an ICPC problem? please post problem link. I think I solved this before. – hasan Aug 10 '15 at 12:04
  • @hasan83- I don't know. This was asked in a contest in my college as a bigger problem which basically reduced to this.The contest was hosted on local network therefore i don't have any link. – LTim Aug 10 '15 at 15:31
  • I posted an O(n log n) answer to this question here: http://stackoverflow.com/a/31945031/1327235 – Juan Lopes Aug 11 '15 at 16:12
  • I fixed my answer and tested it. – hasan Aug 13 '15 at 11:12

8 Answers8

4

I am having hard time trying to explain my solution in words. I will just add the code. It will explain itself:

#include <iostream>
#include <fstream>
using namespace std;

#define max 10000

int main(int argc, const char * argv[]) {

    ifstream input("/Users/appleuser/Documents/Developer/xcode projects/SubArrayCount/SubArrayCount/input.in");

    int n, arr[max], before[max]={0}, after[max]={0}, result[max];
    input >> n;
    for (int i=0; i<n; i++)
        input >> arr[i];

    for (int i=0;i<n;i++)
        for (int j=i-1;j>=0&&arr[j]<arr[i];j-=before[j]+1)
            before[i]+=before[j]+1;

    for (int i=n-1;i>=0;i--)
        for (int j=i+1;j<n&&arr[j]<arr[i];j+=after[j]+1)
            after[i]+=after[j]+1;

    for (int i=0;i<n;i++)
        result[i]= (before[i]+1)*(after[i]+1);

    for (int i=0; i<n; i++)
        cout << result [i] << " ";
    cout << endl;

    return 0;
}

Explanation for (before[i]+1)*(after[i]+1):

for each value we need the numbers lies before and less than the value and the numbers lies after and less than the value.

  | 0  1  2  3  4  5 .... count of numbers less than the value and appears before.
---------------------
0 | 1  2  3  4  5  6
1 | 2  4  6  8  10 12
2 | 3  6  9  12 15 18
3 | 4  8  12 16 20 24
4 | 5  10 15 20 25 30
5 | 6  12 18 24 30 36
. | 
. |
. |
count of numbers less than the value and appears after.

Example: for a number that have 3 values less than it and appears before and have 4 values less than it and appears after. answer is V(3,4) = 20 = (3+1) * (4+1)

please, let me know the results.

Did you manage to find the source link of the problem?

hasan
  • 23,815
  • 10
  • 63
  • 101
  • No source link as contest was hosted on college lan. Btw what would be the worst case complexity of your solution? – LTim Aug 13 '15 at 19:56
  • @hasan83 yup, that's the c++ implementation of the same idea i presented in my post –  Aug 13 '15 at 21:45
  • Maybe @Paul can help me here with the complexity. I think its 2n + 2n + n = O(n) – hasan Aug 13 '15 at 22:02
  • So, how you can test it for a large and worst cases. Do you have the judge input and output? – hasan Aug 13 '15 at 22:04
  • Why don't you ask them at collage for the source of problem or how to test on there input? Do you have the problem name. Maybe we can search for it? – hasan Aug 13 '15 at 22:05
  • @hasan83 i'm horrible at calculating runtime complexitys. Better not rely on me. –  Aug 13 '15 at 22:23
2

You could store SubArrays sizes in another Array (arr2) to save yourself recalculating them.

arr2 must be the length of the max value in the arr1

i.e -

Take the Array {1,2,4,6,7,8}

arr2 is declared like this:

arr2 = []
for i in range(max(arr1)):
    arr2.append(0)

Now, the algorithm goes like this:

Say you hit number 6.

Since 6-1=5 does not exist it has a default value of 0 corresponding to index 5 in arr2, because nothing has been added there yet. So you store 0+1=1 in position 6 of arr2. Then you hit the number 7. You check if 7-1=6 exists in arr2. It does, with a value of 1. Hence add the value of 1+1=2 to position 7 in arr2.

For each value in arr2 we simply add this to the count. We can do so simultaneously with a count variable.

This algorithm is O(n)

  • I don't get it. I need the no of subarrays for EACH element of the original array not the sum.In the given example 1 for 1, 2 for 2, 3 for 3. – LTim Aug 10 '15 at 15:39
  • So `arr2[1] = 1`, `arr2[2]=2`, if no subarrays exist you will get `arr2[x] = 0` –  Aug 10 '15 at 16:37
2

Here is my O(N) time java solution using Stack. Basic idea is to move from left to right keeping track of sub arrays ending at "i" and then right to left keeping track of sub arrays starting from "i":

public int[] countSubarrays(int[] arr) {
Stack<Integer> stack = new Stack<>();
int[] ans = new int[arr.length];
for(int i = 0; i < arr.length; i++) {
  while(!stack.isEmpty() && arr[stack.peek()] < arr[i]) {
    ans[i] += ans[stack.pop()];
  }
  stack.push(i);
  ans[i]++;
}
stack.clear();
int[] temp = new int[arr.length];
 for(int i = arr.length - 1; i >= 0; i--) {
  while(!stack.isEmpty() && arr[stack.peek()] < arr[i]) {
    int idx = stack.pop();
    ans[i] += temp[idx];
    temp[i] += temp[idx];
  }
  stack.push(i);
  temp[i]++;
}
return ans;

}

piyush121
  • 496
  • 11
  • 16
1

Lets look at an example.

{4, 5, 6, 3, 2}

Iterating from the begin till the end we can detect single increasing subarray: {4, 5, 6} and two single elements 3, and 2.

So we're detecting lengths of subarrays 3, 1, and 1. First subarray {4, 5, 6} gives us 6 possible decisions, i.e. 1 + 2 + 3 = 6. It's a key.

For any length of increasing subarray N we can calculate the number of decisions as N * (N + 1)/2.

Therefore we have 3 * (3 + 1)/2 + 1 * (1 + 1)/2 + 1 * (1 + 1)/2, i.e. 6 + 1 + 1 = 8.

While we need a single iteration only, we have O(N) algorithm.

Mark Shevchenko
  • 7,937
  • 1
  • 25
  • 29
1

You haven't specified in which way repeating elements are handled/what that element is (the element at the precise position in the array, or any element in the array with the same value). Assuming the problem is for the element at a precise index this can be solved easily in linear time:

define ctSubarrays(int[] in , int at)
    int minInd = at, maxInd = at;

    //search for the minimum-index (lowest index with a smaller element than in[at]
    for(; minInd > 0 && in[minInd - 1] < in[at] ; minInd--);

    //search for the maximum-index (highest index with a smaller element than in[at]
    for(; maxInd < length(at) - 1 && in[maxInd + 1] < in[at] ; maxInd++);

    //now we've got the length of the largest subarray meeting all constraints
    //next step: get the number of possible subarrays containing in[at]
    int spaceMin = at - minInd;
    int spaceMax = maxInd - at;

    return spaceMin * spaceMax;
1

If the array is sorted,

count = 1;
for (i = 1 to n-1){
    if(a[i-1] == a[i]){
        count = count + 1;
    }else if(a[i-1] + 1 == a[i]){
        count of sub arrays for a[i-1] = count;
        count = count + 1;
    }else{
        count of sub arrays for a[i-1] = count;
        count = 1;
    }
}
count of sub arrays for a[n-1] = count;

If the array is not sorted,

Assumption 3:If the array is like {3,1,2,3} then #sub arrays for 3 is 3

aMin = min(a);//O(n)
aMax = max(a);
len = (aMax - aMin + 1);

create array b of size len;
for (j = 0 to len-1){
    b[j] = 0;
} 

count = 1;
for (i = 1 to n-1){
    if(a[i-1] == a[i]){
        count = count + 1;
    }else if(a[i-1] + 1 == a[i]){
        if(b[a[i-1] - aMin] < count){
            b[a[i-1] - aMin] = count;
        } 
        count = count + 1;
    }else{
        if(b[a[i-1] - aMin] < count){
            b[a[i-1] - aMin] = count;
        } 
        count = 1;
    }
}
if(b[a[n-1] - aMin] < count){
    b[a[n-1] - aMin] = count;
} 

for (i = 0 to n-1){
    count of sub arrays for a[i] = b[a[i] - aMin];
} 

This will work even if the array contains negative integers

If Assumption 3 fails according to your problem, and it is like,

Assumption 4:If the array is like {3,1,2,3} then #sub arrays for 3 is 4

{3}, {1,2,3}, {2,3}, {3}

Modify the above code by replacing

if(b[a[i-1] - aMin] < count){
    b[a[i-1] - aMin] = count;
} 

with this

b[a[i-1] - aMin] = b[a[i-1] - aMin] + count; 
Somabrata
  • 105
  • 11
  • My problem is like assumption 4 but for 3 it is 6.. Adding {3,1,2} and {3,1,2,3} to your answer – LTim Aug 12 '15 at 02:50
1

Create a value-to-index map and traverse from bottom to top - maintain an augmented tree of intervals. Each time an index is added, adjust the appropriate interval and calculate the total from the relevant segment. For example:

A = [5,1,7,2,3] => {1:1, 2:3, 3:4, 5:0, 7:2}

indexes     interval     total sub-arrays with maximum exactly
1           (1,1)        1 =>           1
1,3         (3,3)        2 =>           1 
1,3,4       (3,4)        3 =>           2
1,3,4,0     (0,1)        5 =>           2
1,3,4,0,2   (0,4)        7 => 3 + 2*3 = 9

Insertion and deletion in augmented trees are of O(log n) time-complexity. Worst-case total time-complexity is O(n log n).

גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
-1

Using JavaScript Not sure the Big O notation. But here i'm looping the list. Then starting 2 loops. One counting down from i, and the other counting up from i+1.

let countArray = []
for(let i = 0; i < arr.length; i++){
  let count = 0;
  
  *This will count downwards starting at i*

  for(let j = i; j >= 0; j--){
    if(arr[j] > arr[i]) {break;}
    count++;
  }

  *This will count upwards starting at i+1 so that you dont get a duplicate of the first value*

  for(let j = i+1; j < arr.length; j++){
    if(arr[j] >= arr[i]) {break;}
    count++;
  }
countArray.push(count);
}
return countArray;
Rahul Bhobe
  • 4,165
  • 4
  • 17
  • 32