Count Different Numbers II

该博客主要讨论了一种使用滑动窗口算法来解决寻找给定数组中连续子数组不同元素计数的问题。文章通过示例解释了如何利用递推关系进行计算,并提供了两种不同的C++实现方案。这两种方案均实现了O(n)的时间复杂度,有效地处理了不超过10^4个元素的数组和查询。
摘要由CSDN通过智能技术生成

Description

Given a series of n numbers and q queries width w, find the total number of different numbers of all consecutive w numbers for
each query.

For example: the numbers are {2, 2, 1, 3, 4, 4, 5} and w is 3. All the consecutive w numbers are:

{2, 2, 1}, 2 different numbers 2 and 1;
{2, 1, 3}, 3 different numbers 2, 1 and 3;
{1, 3, 4}, 3 different numbers …
{3, 4, 4}, 2 different numbers …
{4, 4, 5}, 2 different numbers …

The answer is 2 + 3 + 3 + 2 + 2 == 12.

Input

There are no more than 20 test cases. Each case contains three lines.
The first line has two integer numbers n and q.
The second line are n integer numbers.
1 <= n <= 10^ 4 and all the n integer numbers are in range [1, 10^4].

The third line are q integer numbers denote each w.

1 <= q <= 10^4, 1 <= w <= n.

Output

For each case, output q lines, the answer for each query.

Sample Input

7 3
2 2 1 3 4 4 5
2 3 7

Sample Output

10
12
5

Hint

/*
先考虑一个递推关系:当知道宽度为 w - 1 时的答案,每个 w - 1 宽的区间紧挨着之后的那个数,什么情况下是新增的不相同的数?是当这个数上次出现的距离大于w时。

O(n)统计相距任何数相邻两次出现距离为 d 的次数,d为 1, 2, 3, … n

O(n)基于刚刚的统计得到相邻两次出现距离 d 以上的次数。 sumDis[d] 表示同一个数相邻两次出现距离在 d 以上的情况发生的次数。

从宽度 1 开始递推宽度为 w 的答案dp[w],dp[w] = dp[w - 1] + sumDis[w] - sumPostfix,sumPostfix 为 w - 1 长度的后缀中不相同的数的个数,也由递推得到,减去它是因为递推时当宽度加1,总的段数是少1的,就是末尾的那一段 w - 1的区间的统计量要去掉。
*/

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
const int maxn = 1e4 + 10;
int n, q, w;
int a[maxn];
int dp[maxn];
int last[maxn], sumDis[maxn], sumPostfix;
int main()
{
    while(scanf("%d%d", &n, &q) != EOF)
    {
        memset(last, -1, sizeof(last));
        memset(sumDis, 0, sizeof(sumDis));
        dp[1] = n;
        sumPostfix = 0;
        for(int i = 0; i < n; i ++)
        {
            scanf("%d", &a[i]);
            sumDis[i - last[a[i]]] ++;
            last[a[i]] = i;
        }
        for(int i = n - 1; i; i --)
            sumDis[i] += sumDis[i + 1];
        for(int i = 2; i <= n; i ++)
        {
            sumPostfix += last[a[n - i + 1]] == n - i + 1;
            dp[i] = dp[i - 1] + sumDis[i] - sumPostfix;
        }
        while(q --)
        {
            scanf("%d", &w);
            printf("%d\n", dp[w]);
        }
    }
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

int a[10005], last[10005], d[10005], f[10005];
signed main()
{
    ios::sync_with_stdio(false); //取消cin与stdin的同步,提高cin的输入效率
    int n, q;
    while (cin >> n >> q)
    {
        memset(last, -1, sizeof(last));
        memset(d, 0, sizeof(d));
        memset(f, 0, sizeof(f));
        for (int i = 0; i < n; i++)
        {
            cin >> a[i];
            d[i - last[a[i]]]++;
            last[a[i]] = i;
        }
        for (int i = n - 1; i >= 1; i--)
        {
            d[i] += d[i + 1];
        }
        f[1] = n;
        int sumPost = 0;
        for (int i = 2; i <= n; i++)
        {
            sumPost += (last[a[n - i + 1]] == n - i + 1);
            f[i] = f[i - 1] + d[i] - sumPost;
        }
        while (q--)
        {
            int w;
            cin >> w;
            printf("%lld\n", f[w]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值