📌  相关文章
📜  数组范围查询频率与值相同的元素

📅  最后修改于: 2021-04-17 10:45:31             🧑  作者: Mango

给定一个由N个数字组成的数组,任务是回答以下类型的Q个查询:-

query(start, end) = Number of times a 
number x occurs exactly x times in a 
subarray from start to end

例子:

方法1(蛮力)
计算每个查询下子数组中每个元素的频率。如果在每个查询覆盖的子数组中任何数字x的频率为x,我们将增加计数器。

C++
/* C++ Program to answer Q queries to 
   find number of times an element x 
   appears x times in a Query subarray */
#include 
using namespace std;
  
/* Returns the count of number x with
   frequency x in the subarray from 
   start to end */
int solveQuery(int start, int end, int arr[])
{
    // map for frequency of elements
    unordered_map frequency;
  
    // store frequency of each element 
    // in arr[start; end]
    for (int i = start; i <= end; i++) 
        frequency[arr[i]]++;    
  
    // Count elements with same frequency
    // as value
    int count = 0;
    for (auto x : frequency) 
        if (x.first == x.second) 
            count++;    
    return count;
}
  
int main()
{
    int A[] = { 1, 2, 2, 3, 3, 3 };
    int n = sizeof(A) / sizeof(A[0]);
  
    // 2D array of queries with 2 columns
    int queries[][3] = { { 0, 1 },
                         { 1, 1 },
                         { 0, 2 },
                         { 1, 3 },
                         { 3, 5 },
                         { 0, 5 } };
  
    // calculating number of queries
    int q = sizeof(queries) / sizeof(queries[0]);
  
    for (int i = 0; i < q; i++) {
        int start = queries[i][0];
        int end = queries[i][1];
        cout << "Answer for Query " << (i + 1)
             << " = " << solveQuery(start,
             end, A) << endl;
    }
  
    return 0;
}


Java
/* Java Program to answer Q queries to 
find number of times an element x 
appears x times in a Query subarray */
import java.util.HashMap;
import java.util.Map;
  
class GFG 
{
  
  
/* Returns the count of number x with
frequency x in the subarray from 
start to end */
static int solveQuery(int start, int end, int arr[])
{
    // map for frequency of elements
    Map mp = new HashMap<>();
  
    // store frequency of each element 
    // in arr[start; end]
    for (int i = start; i <= end; i++) 
        mp.put(arr[i],mp.get(arr[i]) == null?1:mp.get(arr[i])+1); 
  
    // Count elements with same frequency
    // as value
    int count = 0;
    for (Map.Entry entry : mp.entrySet()) 
        if (entry.getKey() == entry.getValue()) 
            count++; 
    return count;
}
  
// Driver code
public static void main(String[] args) 
{
    int A[] = { 1, 2, 2, 3, 3, 3 };
    int n = A.length;
  
    // 2D array of queries with 2 columns
    int [][]queries = { { 0, 1 },
                        { 1, 1 },
                        { 0, 2 },
                        { 1, 3 },
                        { 3, 5 },
                        { 0, 5 } };
  
    // calculating number of queries
    int q = queries.length;
  
    for (int i = 0; i < q; i++) 
    {
        int start = queries[i][0];
        int end = queries[i][1];
        System.out.println("Answer for Query " + (i + 1)
            + " = " + solveQuery(start,
            end, A));
    }
}
}
  
// This code is contributed by Rajput-Ji


Python3
# Python 3 Program to answer Q queries 
# to find number of times an element x 
# appears x times in a Query subarray
import math as mt
  
# Returns the count of number x with
# frequency x in the subarray from 
# start to end
def solveQuery(start, end, arr):
  
    # map for frequency of elements
    frequency = dict()
  
    # store frequency of each element 
    # in arr[start end]
    for i in range(start, end + 1):
  
  
        if arr[i] in frequency.keys():
            frequency[arr[i]] += 1
        else:
            frequency[arr[i]] = 1
                  
    # Count elements with same 
    # frequency as value
    count = 0
    for x in frequency: 
        if x == frequency[x]: 
            count += 1
    return count
  
# Driver code 
A = [1, 2, 2, 3, 3, 3 ]
n = len(A)
  
# 2D array of queries with 2 columns
queries = [[ 0, 1 ], [ 1, 1 ],
           [ 0, 2 ], [ 1, 3 ],
           [ 3, 5 ], [ 0, 5 ]]
  
# calculating number of queries
q = len(queries)
  
for i in range(q):
    start = queries[i][0]
    end = queries[i][1]
    print("Answer for Query ", (i + 1), 
          " = ", solveQuery(start,end, A))
            
# This code is contributed 
# by Mohit kumar 29


C#
// C# Program to answer Q queries to 
// find number of times an element x 
// appears x times in a Query subarray
using System;
using System.Collections.Generic;
  
class GFG
{
    /* Returns the count of number x with 
    frequency x in the subarray from 
    start to end */
    public static int solveQuery(int start, 
                                 int end, 
                                 int[] arr)
    {
  
        // map for frequency of elements 
        Dictionary mp = new Dictionary();
  
        // store frequency of each element 
        // in arr[start; end] 
        for (int i = start; i <= end; i++)
        {
            if (mp.ContainsKey(arr[i]))
                mp[arr[i]]++;
            else
                mp.Add(arr[i], 1);
        }
  
        // Count elements with same frequency 
        // as value 
        int count = 0;
        foreach (KeyValuePair entry in mp)
        {
            if (entry.Key == entry.Value)
                count++;
        }
        return count;
    }
  
    // Driver code 
    public static void Main(String[] args)
    {
        int[] A = { 1, 2, 2, 3, 3, 3 };
        int n = A.Length;
  
        // 2D array of queries with 2 columns 
        int[,] queries = {{ 0, 1 }, { 1, 1 },
                          { 0, 2 }, { 1, 3 },
                          { 3, 5 }, { 0, 5 }};
  
        // calculating number of queries 
        int q = queries.Length;
  
        for (int i = 0; i < q; i++)
        {
            int start = queries[i, 0];
            int end = queries[i, 1];
            Console.WriteLine("Answer for Query " + (i + 1) +
                              " = " + solveQuery(start, end, A));
        }
    }
}
  
// This code is contributed by
// sanjeev2552


输出:
Answer for Query 1 = 1
Answer for Query 2 = 0
Answer for Query 3 = 2
Answer for Query 4 = 1
Answer for Query 5 = 1
Answer for Query 6 = 3

该方法的时间复杂度为O(Q * N)

方法2(高效)
我们可以使用MO的算法解决此问题。
我们为每个查询分配开始索引,结束索引和查询编号,每个查询采用以下形式:

首先,我们将查询分为多个块,然后使用自定义比较器对查询进行排序。
现在,我们离线处理查询,在每个传入查询中保留两个指针,即MO_RIGHTMO_LEFT ,向前和向后移动这些指针,并根据当前查询的开始和结束索引插入和删除元素。

让当前运行的答案为current_ans

每当我们插入一个元素时,我们都会增加包含元素的频率,如果该频率等于我们刚刚包含的元素,则我们会增加current_ans。如果该元素的频率变为(当前元素+ 1),则意味着该元素的位置更早当current_ans等于其频率时被计入current_ans,因此在这种情况下我们需要减小current_ans。

每当我们删除/删除元素时,我们都会减小被排除元素的频率,如果该频率等于我们刚刚排除的元素,则我们会增加current_ans。如果该元素的频率变为(current element – 1),则意味着当此元素等于它的频率时,该元素就计入current_ans中,因此在这种情况下,我们需要减小current_ans。

/* C++ Program to answer Q queries to
   find number of times an element x 
   appears x times in a Query subarray */
#include 
using namespace std;
  
// Variable to represent block size. 
// This is made global so compare() 
// of sort can use it.
int block;
  
// Structure to represent a query range
struct Query {
    int L, R, index;
};
  
/* Function used to sort all queries 
   so that all queries of same block
   are arranged together and within 
   a block, queries are sorted in 
   increasing order of R values. */
bool compare(Query x, Query y)
{
    // Different blocks, sort by block.
    if (x.L / block != y.L / block)
        return x.L / block < y.L / block;
  
    // Same block, sort by R value
    return x.R < y.R;
}
  
/* Inserts element (x) into current range
   and updates current answer */
void add(int x, int& currentAns, 
         unordered_map& freq)
{
  
    // increment frequency of this element
    freq[x]++;
  
    // if this element was previously 
    // contributing to the currentAns,
    // decrement currentAns
    if (freq[x] == (x + 1))
        currentAns--;
  
    // if this element has frequency 
    // equal to its value, increment
    // currentAns
    else if (freq[x] == x)
        currentAns++;
}
  
/* Removes element (x) from current 
   range btw L and R and updates 
   current Answer */
void remove(int x, int& currentAns, 
            unordered_map& freq)
{
  
    // decrement frequency of this element
    freq[x]--;
  
    // if this element has frequency equal 
    // to its value, increment currentAns
    if (freq[x] == x)
        currentAns++;
  
    // if this element was previously 
    // contributing to the currentAns 
    // decrement currentAns
    else if (freq[x] == (x - 1)) 
        currentAns--;
}
  
/* Utility Function to answer all queries
   and build the ans array in the original 
   order of queries */
void queryResultsUtil(int a[], Query q[], 
                        int ans[], int m)
{
  
    // map to store freq of each element
    unordered_map freq;
  
    // Initialize current L, current R
    // and current sum
    int currL = 0, currR = 0;
    int currentAns = 0;
  
    // Traverse through all queries
    for (int i = 0; i < m; i++) {
        // L and R values of current range
        int L = q[i].L, R = q[i].R; 
        int index = q[i].index;
  
        // Remove extra elements of previous
        // range. For example if previous 
        // range is [0, 3] and current range 
        // is [2, 5], then a[0] and a[1] are 
        // removed
        while (currL < L) {
            remove(a[currL], currentAns, freq);
            currL++;
        }
  
        // Add Elements of current Range
        while (currL > L) {
            currL--;
            add(a[currL], currentAns, freq);
        }
        while (currR <= R) {
            add(a[currR], currentAns, freq);
            currR++;
        }
  
        // Remove elements of previous range.  For example
        // when previous range is [0, 10] and current range
        // is [3, 8], then a[9] and a[10] are Removed
        while (currR > R + 1) {
            currR--;
            remove(a[currR], currentAns, freq);
        }
  
        // Store current ans as the Query ans for
        // Query number index
        ans[index] = currentAns;
    }
}
  
/* Wrapper for queryResultsUtil() and outputs the
   ans array constructed by answering all queries */
void queryResults(int a[], int n, Query q[], int m)
{
    // Find block size
    block = (int)sqrt(n);
  
    // Sort all queries so that queries of same blocks
    // are arranged together.
    sort(q, q + m, compare);
  
    int* ans = new int[m];
    queryResultsUtil(a, q, ans, m);
  
    for (int i = 0; i < m; i++) {
        cout << "Answer for Query " << (i + 1)
             << " = " << ans[i] << endl;
    }
}
  
// Driver program
int main()
{
    int A[] = { 1, 2, 2, 3, 3, 3 };
  
    int n = sizeof(A) / sizeof(A[0]);
  
    // 2D array of queries with 2 columns
    Query queries[] = { { 0, 1, 0 },
                        { 1, 1, 1 },
                        { 0, 2, 2 },
                        { 1, 3, 3 },
                        { 3, 5, 4 },
                        { 0, 5, 5 } };
  
    // calculating number of queries
    int q = sizeof(queries) / sizeof(queries[0]);
  
    // Print result for each Query
    queryResults(A, n, queries, q);
  
    return 0;
}
输出:
Answer for Query 1 = 1
Answer for Query 2 = 0
Answer for Query 3 = 2
Answer for Query 4 = 1
Answer for Query 5 = 1
Answer for Query 6 = 3

使用MO算法的这种方法的时间复杂度为O(Q * sqrt(N)* logA) ,其中logA是为每个查询将元素A插入unordered_map中的复杂度。