1

The problem is: You are given an array of size N. Also given q=number of queries; in queries you will be given l=lower range, u=upper range and num=the number of which you will have to count frequency into l~u.

I've implemented my code in C++ as follows:

#include <iostream>
#include <map>

using namespace std;

map<int,int>m;

void mapnumbers(int arr[], int l, int u)
{
    for(int i=l; i<u; i++)
    {
        int num=arr[i];
        m[num]++;
    }
}


int main()
{
    int n; //Size of array
    cin>>n;

    int arr[n];

    for(int i=0; i<n; i++)
        cin>>arr[i];

    int q; //Number of queries
    cin>>q;

    while(q--)
    {
        int l,u,num;   //l=lower range, u=upper range, num=the number of which we will count frequency
        cin>>l>>u>>num;
        mapnumbers(arr,l,u);
        cout<<m[num]<<endl;
    }

    return 0;
}

But my code has a problem, in each query it doesn't make the map m empty. That's why if I query for the same number twice/thrice it adds the count of frequency with the previous stored one.

How do I solve this? Will it be a poor program for a large range of query as 10^5? What is an efficient solution for this problem?

Max
  • 1,810
  • 3
  • 26
  • 37
user283207
  • 11
  • 1
  • 4
  • 2
    Not using a global variable? – Daniel Daranas May 05 '15 at 15:34
  • 1
    Yes it will have poor performance for a large number of queries, because atm you are traversing the array for each query, whereas in the extreme it is sufficient to traverse the array only once. – 463035818_is_not_an_ai May 05 '15 at 15:34
  • How can I solve this by traversing the array only once? @tobi303 – user283207 May 05 '15 at 15:52
  • It is possible but not very practical. You could create an `[maxElement][size][size]` sized structure, where you store for each number (first index) the number of occurences between the lower range (second index) and the upper range (last index). This will waste lots of memory and building this structure will take some time, but once you have it, its just a matter of accesing the elements. This is just the extreme case of trading memory vs computations. The optimal algorithm will be something in between this and yours. Very much depends on the details, size of the array, number of queries, etc – 463035818_is_not_an_ai May 05 '15 at 15:59
  • Is there any way in which I'll calculate the frequencies by traversing the array only once and calculate each query by using any method like cumulative sum? – user283207 May 05 '15 at 16:02

4 Answers4

2

You can solve the task using SQRT-decomposition of queries. The complexity will be O(m*sqrt(n)). First of all, sort all queries due to the following criteria: L/sqrt(N) should be increasing, where L is the left bound of query. For equal L/sqrt(N), R (right bounds) should be increasing too. N is the number of queries. Then do this: calculate answer for first query. Then, just move the bounds of this query to the bounds of the next query one by one. For example, if your first query after sort is [2,7] and second is [1, 10], move left bound to 1 and decrease the frequency of a[2], increase the frequency of a1. Move the right bound from 7 to 10. Increase the frequency of a[8], a[9] and a[10]. Increase and decrease frequencies using your map. This is a very complicated technique, but it allows to solve your task with good complexity. You can read more about SQRT-decomposition of queries here: LINK

  • That's too advanced for this problem, the array does not change here and a simple sort is sufficient. – Petr May 05 '15 at 16:06
0

To clear the map, you need to call map::clear():

void mapnumbers(int arr[], int l, int u)
{
    m.clear()

A better approach to the clearing problem is to make m a local variable for the while (q--) loop, or even for the mapnumbers function.

However, in general it is very strange why you need map at all. You traverse the whole array anyway, and you know the number you need to count, so why not do

int mapnumbers(int arr[], int l, int u, int num)
{
    int result = 0;
    for(int i=l; i<u; i++)
    {
        if (arr[i] == num);
            result ++;
    }
    return result;
}

This will be faster, even asymptotically faster, as map operations are O(log N), so your original solution ran for O(N log N) per query, while this simple iteration runs for O(N).

However, for a really big array and many queries (I guess the problem comes from some competitive programming site, does not it?), this still will not be enough. I guess there should be some data structure and algorithm that allows for O(log N) query, though I can not think of any right now.

UPD: I have just realized that the array does not change in your problem. This makes it much simpler, allowing for a simple O(log N) per query solution. You just need to sort all the numbers in the input array, remembering their original positions too (and making sure the sort is stable, so that the original positions are in increasing order); you can do this only once. After this, every query can be solved with just two binary searches.

Petr
  • 9,812
  • 1
  • 28
  • 52
  • Thank you. I understand. Is there any way in which I'll calculate the frequencies by traversing the array only once and calculate each query by using any method like cumulative sum? – user283207 May 05 '15 at 16:00
0

Many Algorithms are available for this kind of problems . This looks like a straight forward data structure problem . You can use Segment tree , Square Root Decomposition . Check Geeksforgeeks for the algorithm ! The reason i am telling you to learn algorithm is , this kind of problems have such large constrains , your verdict will be TLE if you use your method . So better using Algorithms .

CodeHead
  • 177
  • 1
  • 2
  • 12
0

Many answers here are much way complicated. I am going to tell you easy way to find range frequency. You can use the binary search technique to get the answer in O(logn) per query.

For that, use arrays of vector to store the index values of all numbers present in the array and then use lower_bound and upper_bound provided by C++ STL.

Here is C++ Code:

    #define MAX 1000010

    std::vector<int> v[MAX];

    int main(){

    cin>>n;

    for (int i = 0; i < n; ++i)
    {
        cin>>a;
        v[a].push_back(i);
    }

    int low = 0, high = 0;

    int q; //Number of queries
    cin>>q;

    while(q--)
    {
        int l,u,num;   //l=lower range, u=upper range, num=the number of which we will count frequency
        cin>>l>>u>>num;
        low = lower_bound(v[num].begin(), v[num].end(), l) - v[num].begin();
        high = upper_bound(v[num].begin(), v[num].end(), u) - v[num].begin();
        cout<<(high - low)<<endl;
    }

    return 0;
}

Overall Time Complexity: O(Q*log n)

Vivek Shah
  • 17
  • 3
  • 6