最多有多少个连续子数组。 n 个唯一编号

Posted

技术标签:

【中文标题】最多有多少个连续子数组。 n 个唯一编号【英文标题】:How many contiguous subarrays with max. n unique numbers 【发布时间】:2019-03-06 11:50:05 【问题描述】:

我在网上发现了一个编程挑战,想知道是否有更有效的解决方案。

问题: 您将获得一个包含 n 个数字的列表以及一个数字 X,它指的是可以包含在连续子数组中的不同数字的最大数量。我们需要统计所有满足 X 条件的连续子​​数组。

输入 第一行是两个数字 n 和 x;子数组中的数字数量和唯一数字的最大数量。

例子:

5 2
1 2 3 1 1
ans = 10
explanation: ([1],[2],[3],[1],[1],[1,2],[2,3],[3,1],[1,1],[3,1,1])

我的方法 使用两个循环遍历列表的所有子数组,并计算相关子数组中唯一数字的数量(使用集合)。当然,必须有一种更有效的方法来计算这个?抱歉,如果这个问题不属于这里,请随时编辑。

编辑:nellex 的更正代码有时会给出错误的答案

int main() 
    int n, x;
    cin >> n >> x;

    vector<int> a;
    for (int i = 1; i <= n; i++) 
        int b;
        cin >> b;
        a.push_back(b);
    

    int ans = 0, end = 1;
    set<int> uniq;
    map<int, int> freq;
    for (int start = 0; start < n; start++) 
        cout << start << " and end=" << end << endl;
        while (uniq.size() <= x && end < n) 
            if (uniq.size() == x && freq[a[end]] == 0) 
                break;
            
            uniq.insert(a[end]);
            freq[a[end]]++;
            end++;
        
        cout << "added " << end << " - " << start << " to ans" << endl;
        ans += end - start;
        freq[a[start]]--;
        if (freq[a[start]] == 0) 
            uniq.erase(a[start]);
        
    
    cout << ans;

编辑: 第一个测试用例约束:

1≤k≤n≤100

1≤xi≤10

最大的限制:

1≤k≤n≤5⋅10^5

1≤xi≤10^9

【问题讨论】:

我很困惑。问题是什么?为什么 x 2 是三位数?如果我们想要唯一的数字,为什么答案包含三遍[1]?,... 在示例中,问题是列表 [1,2,3,1,1] 的子数组中有多少包含最大值。 2 个唯一编号。对不起,如果我不清楚。 为什么你认为你正在做的事情是低效的。也就是说,我删除了 C++ 标签,因为这无论如何都不是关于 C++ 的问题。 @UlrichEckhardt 因为有大的测试用例(例如 10^5)我的代码超过了时间限制。 @Quiti 我看到你修改了代码有点不正确。如果您将数组索引为 0,请在初始化期间设置 int end = 0。同样对于给定的约束,您需要使用long long 来回答。 【参考方案1】:

滑动窗口方法将成为解决此问题的更好解决方案,这将使我们能够通过使用 Set 和 Map 在 O(n*log(n)) 中解决它:@987654321 @

int main() 
    int n, x;
    cin >> n >> x;

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

    int end = 0;
    long long ans = 0;

    set<int> uniq;
    map<int, int> freq;
    for(int start = 0; start < n; start++) 
        while(uniq.size() <= x && end < n) 
            if(uniq.size() == x && freq[a[end]] == 0) 
                break;
            
            uniq.insert(a[end]);
            freq[a[end]]++;
            end++;
        
        ans += end - start;
        freq[a[start]]--;
        if(freq[a[start]] == 0) 
            uniq.erase(a[start]);
        
    
    cout << ans;

该算法的工作方式是,对于由索引 starta[start] 定义的每个元素,我们尝试找到从start 开始的最大子数组,这样唯一的子数组中的元素是 。如果标识的子数组的大小为 S,那么我们知道元素 a[start] 将成为从索引 start 开始的 S 个子数组的一部分。

如果我们对给定的示例进行试运行,

当 start = 1 时,我们将生成子数组 [1], [1, 2] 当 start = 2 时,我们将生成子数组 [2], [2, 3] 当 start = 3 时,我们将生成子数组 [3], [3, 1], [3, 1, 1] 当 start = 4 时,我们将生成子数组 [1], [1, 1] 当 start = 5 时,我们将生成子数组 [1]

【讨论】:

我还在想,你怎么声明 int a[n+1]? Visual Studio 说“表达式必须有一个常量值”。否则,谢谢。 我猜它是与 C++11 标准一起引入的,不过不太确定。另外,我意识到我的代码中有一个错误,我现在已经修复了。你可能想再看看它。我的道歉=/ 仍然出现超出范围的错误,您确定已修复吗?我真的无法弄清楚它来自哪里...... 为什么从1开始呢?我喜欢这个主意,但代码似乎有点不完整。 @Quiti 你能分享一下程序失败的测试用例吗?【参考方案2】:

我们可以在O(n) 时间内通过保留两个指针p_lp_r 来解决这个问题,这两个指针都在数组中向上移动,同时为我们遇到的每个元素更新频率计数h[e]作为当前唯一项目的数量,k

例如:

5 2
1 2 3 1 1

让我们看看每次迭代

k = 0
h = 
total = 0
p_r = -1
p_l = -1

1:   p_r = 0
     h = 1:1
     k = 1
     total = 1

2:   p_r = 1
     h = 1:1, 2:1
     k = 2
     total = 1 + 2 = 3

3:   p_r = 2
     h = 1:1, 2:1, 3:1
     k = 3

  => move p_l until k equals X:
     p_l = 0
     h = 1:1-1=0, 2:1, 3:1
     k = 3 - 1 = 2

     total = 3 + 2 = 5

1:   p_r = 3
     h = 1:1, 2:1, 3:1
     k = 3

  => move p_l until k equals X:
     p_l = 1
     h = 1:1, 2:1-1=0, 3:1
     k = 3 - 1 = 2

     total = 5 + 2 = 7

1:   p_r = 4
     h = 1:2, 2:0, 3:1
     k = 2
     total = 7 + 3 = 10

【讨论】:

以上是关于最多有多少个连续子数组。 n 个唯一编号的主要内容,如果未能解决你的问题,请参考以下文章

最多具有K个元素的最大连续子数组

leetcode每日一题(2021.5.13)——最大子序扣

题解子数组有主元素

2021-09-03:直线上最多的点数。给你一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。力扣149。(代码片

最大连续子数组(元素数量最多)

深度为k的二叉树至多有多少个结点