Queries to check if any non-repeating element exists within range [L, R] of an Array
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:
- Store the previous occurrence and next occurrence of every ith element in the array as pair.
- Build the merge sort tree and merge nodes in them according to the previous occurrence. The merge function is used to merge the ranges.
- 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.
- For answering the query, we need node with previous occurrence strictly less than l.
- 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); } |
No Yes
Time Complexity: O(N*log(N) + Q*log2(N))
Auxiliary Space: O(N*log(N))
Related Topic: Segment Tree
Please Login to comment...