Skip to content
Related Articles
Open in App
Not now

Related Articles

Queries to check if any non-repeating element exists within range [L, R] of an Array

Improve Article
Save Article
  • Difficulty Level : Medium
  • Last Updated : 11 Jul, 2022
Improve Article
Save Article

Given an array arr[] consisting of integers and queries Q of the form (L, R), the task is to check whether any non-repeating element is present within indices [L, R](1 based indexing) or not. If there is at least one non-repeating element, then print “Yes”. Otherwise, print “No”.

Examples:

Input: arr[] = {1, 2, 1, 2, 3, 4}, Queries[][] = {{1, 4}, {1, 5}}
Output: No Yes
Explanation:
For the first query, the subarray is {1, 2, 1, 2}, we can see that both number have frequency 2. Therefore, the answer is No.
For the second query, the subarray is {1, 2, 1, 2, 3}, we can see that 3 has frequency 1 so the answer is Yes.

Input: arr[] = {1, 2, 3, 4, 5}, Queries[][] = {{1, 4}}
Output: Yes
Explanation: The subarray is {1, 2, 3, 4}, has all elements as frequency 1 so the answer is Yes.

Naive Approach:
The simplest approach to solve the problem is to iterate over a given subarray for each query and maintain a map for storing the frequency of each element. Iterate over the map and check whether there is an element of frequency 1 or not.

Time Complexity: O(Q * N)
Auxiliary Space Complexity: O(N)

Efficient Approach: The key observation for the solution is, for the element to have frequency 1 in the given array, the previous occurrence of this number in the array is strictly less than the query l and next occurrence of the element is strictly greater than r of some query. Use this observation to find order. Below are the steps that use the Merge Sort Tree approach to solve the given problem:

  1. Store the previous occurrence and next occurrence of every ith element in the array as pair
  2. Build the merge sort tree and merge nodes in them according to the previous occurrence. The merge function is used to merge the ranges.
  3. At each node of merge sort tree, maintain prefix maximum on the next occurrence because we need as smallest possible previous occurrence and as big next occurrence of some element.
  4. For answering the query, we need node with previous occurrence strictly less than l. 
  5. For the element in the merge sort tree with the previous occurrence less than l, find the max next occurrence and check if next occurrence is greater than r of the query, then there is an element present in the subarray with frequency 1.

Below is the implementation of the above approach:

CPP




// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
  
const int INF = 1e9 + 9;
const int N = 1e5 + 5;
  
// Merge sort of pair type for storing
// prev and next occurrences of element
vector<vector<pair<int, int> > > segtree(4 * N);
  
// Stores the occurrences
vector<pair<int, int> > occurrences(N);
  
// Finds occurrences
vector<set<int> > pos(N);
  
int n;
  
// Function to build merge sort tree
void build(int node = 0, int l = 0,
           int r = n - 1)
{
  
    // For leaf node, push the prev &
    // next occurrence of the lth node
    if (l == r) {
        segtree[node].push_back(occurrences[l]);
        return;
    }
  
    int mid = (l + r) / 2;
  
    // Left recursion call
    build(2 * node + 1, l, mid);
  
    // Right recursion call
    build(2 * node + 2, mid + 1, r);
  
    // Merging the left child and right child
    // according to the prev occurrence
    merge(segtree[2 * node + 1].begin(),
          segtree[2 * node + 1].end(),
          segtree[2 * node + 2].begin(),
          segtree[2 * node + 2].end(),
          back_inserter(segtree[node]));
  
    // Update the next occurrence
    // with prefix maximum
    int mx = 0;
  
    for (auto& i : segtree[node]) {
  
        // Update the maximum
        // next occurrence
        mx = max(mx, i.second);
  
        // Update the next occurrence
        // with prefix max
        i.second = mx;
    }
}
  
// Function to check whether an
// element is present from x to y
// with frequency 1
bool query(int x, int y, int node = 0,
           int l = 0, int r = n - 1)
{
    // No overlap condition
    if (l > y || r < x || x > y)
        return false;
  
    // Complete overlap condition
    if (x <= l && r <= y) {
  
        // Find the first node with
        // prev occurrence >= x
        auto it = lower_bound(segtree[node].begin(),
                              segtree[node].end(),
                              make_pair(x, -1));
  
        // No element in this range with
        // previous occurrence less than x
        if (it == segtree[node].begin())
            return false;
  
        else {
  
            it--;
  
            // Check if the max next
            // occurrence is greater
            // than y or not
            if (it->second > y)
                return true;
            else
                return false;
        }
    }
  
    int mid = (l + r) / 2;
    bool a = query(x, y, 2 * node + 1, l, mid);
    bool b = query(x, y, 2 * node + 2, mid + 1, r);
  
    // Return if any of the
    // children returned true
    return (a | b);
}
  
// Function do preprocessing that
// is finding the next and previous
// occurrences
void preprocess(int arr[])
{
  
    // Store the position of
    // every element
    for (int i = 0; i < n; i++) {
        pos[arr[i]].insert(i);
    }
  
    for (int i = 0; i < n; i++) {
  
        // Find the previous
        // and next occurrences
        auto it = pos[arr[i]].find(i);
        if (it == pos[arr[i]].begin())
            occurrences[i].first = -INF;
  
        else
            occurrences[i].first = *prev(it);
  
        // Check if there is no next occurrence
        if (next(it) == pos[arr[i]].end())
  
            occurrences[i].second = INF;
        else
            occurrences[i].second = *next(it);
    }
  
    // Building the merge sort tree
    build();
}
  
// Function to find whether there is a
// number in the subarray with 1 frequency
void answerQueries(int arr[],
                   vector<pair<int, int> >& queries)
{
    preprocess(arr);
  
    // Answering the queries
    for (int i = 0; i < queries.size(); i++) {
        int l = queries[i].first - 1;
        int r = queries[i].second - 1;
  
        bool there = query(l, r);
  
        if (there == true)
            cout << "Yes\n";
  
        else
            cout << "No\n";
    }
}
  
// Driver Code
int main()
{
    int arr[] = { 1, 2, 1, 2, 3, 4 };
    n = sizeof(arr) / sizeof(arr[0]);
  
    vector<pair<int, int> > queries = { { 1, 4 }, { 1, 5 } };
  
    answerQueries(arr, queries);
}


Output:

No
Yes

Time Complexity: O(N*log(N) + Q*log2(N))
Auxiliary Space: O(N*log(N))

Related Topic: Segment Tree


My Personal Notes arrow_drop_up
Related Articles

Start Your Coding Journey Now!