【问题标题】:Count subarrays divisible by K计算可被 K 整除的子数组
【发布时间】:2016-08-17 04:29:14
【问题描述】:

给定一个由 n 个正整数组成的序列,我们需要计算其和可被 k 整除的连续子序列。

约束:N 最大为 10^6,每个元素最大为 10^9,K 最大为 100

示例:设 N=5 和 K=3 且数组为 1 2 3 4 1

这里的答案是 4

解释:存在4个子序列,其和能被3整除,它们是

3
1 2
1 2 3
2 3 4

我的尝试:

long long int count=0;
for(int i=0;i<n;i++){
    long long int sum=0;
    for(int j=i;j<n;j++)
    {
        sum=sum+arr[j];
        if(sum%k==0)
        {
            count++;
        }
    }
}

但显然它的方法很糟糕。他们可以更好地解决这个问题吗?请帮忙。

完整问题:https://www.hackerrank.com/contests/w6/challenges/consecutive-subsequences

【问题讨论】:

  • 你要子序列还是子数组
  • @AyushJain 连续子序列,如前所述
  • 假设你的数组是[1,4,3,2,1],那么你期望什么输出。你能告诉我吗?
  • 如果您取所有输入数字的 %K 并使用休止符,您可以简化计算。因此,您将使用低于 100 的数字,这将帮助您避免溢出。
  • 我有一个较慢的解决方案,即 O(NK),但它更容易理解,如果您愿意,我可以发布它

标签: c++ algorithm


【解决方案1】:

这是一个快速的 O(n + k) 解决方案:

1) 计算前缀和 pref[i](for 0

2)现在我们可以计算 count[i]——总和为 i 模 k(0

3)答案是所有 i 的 sum count[i] * (count[i] - 1) / 2。

4) 最好以 k 为模计算前缀和以避免溢出。

为什么有效?让我们仔细看看可被 k 整除的子数组。假设它从 L 位置开始并在 R 位置结束。它可以被 k 整除当且仅当 pref[L - 1] == pref[R] (模 k) 因为它们的差是模 k 为零(根据可除性的定义)。所以对于每个固定的模数,我们可以选择任意两个前缀和这个前缀和模数 k(并且恰好有 count[i] * (count[i] - 1) / 2 种方法)。

这是我的代码:

long long get_count(const vector<int>& vec, int k) {
  //Initialize count array.
  vector<int> cnt_mod(k, 0);
  cnt_mod[0] = 1;
  int pref_sum = 0;
  //Iterate over the input sequence.
  for (int elem : vec) {
    pref_sum += elem;
    pref_sum %= k;
    cnt_mod[pref_sum]++;
  }
  //Compute the answer.
  long long res = 0;
  for (int mod = 0; mod < k; mod++)
    res += (long long)cnt_mod[mod] * (cnt_mod[mod] - 1) / 2;
  return res;
}

【讨论】:

  • 为什么要计算[pref[i] % k]++.?
  • 通过 count[i] 的定义——总和为 k 的前缀数。
  • 不是说它不起作用,你能不能给我们慢的人把它当哑巴?
  • Hrm...我一定很困惑,你能在例子上运行它吗? A={1,2,3,4,1}, pref={1,3,6,10,11}, perf%3={1,0,0,1,2},count={2,2 ,1}, sum count[i]*(count[i]-1)/2=1+1+0=2 != 4.
  • count[0] 最初是一个空前缀。 count={3,2,1},总和为 4。
【解决方案2】:

这必须使您的计算更容易:

//Now we will move all numbers to [0..K-1]
long long int count=0;
for(int i=0;i<n;i++){
    arr[i] = arr[i]%K;
}

//Now we will calculate cout of all shortest subsequences.
long long int sum=0; 
int first(0); 
std::vector<int> beg;
std::vector<int> end;
for(int i=0;i<n;i++){
    if (arr[i] == 0)
    {
        count++; 
        continue;
    }
    sum += arr[i];
    if (sum == K)
    {
        beg.push_back(first);
        end.push_back(i);
        count++;
    }
    else
    {
        while (sum > K)
        {
            sum -= arr[first];
            first++;     
        }
        if (sum == K)
        {
            beg.push_back(first);
            end.push_back(i);
            count++;
        }
    }        
}

//this way we found all short subsequences. And we need to calculate all subsequences that consist of some short subsequencies.
int party(0);
for (int i = 0; i < beg.size() - 1; ++i)
{
    if (end[i] == beg[i+1])
    {
        count += party + 1;
        party++;
    }
    else
    {
        party = 0;
    } 
}

因此,最大数组大小 = 10^6 和最大剩余大小 = 99,即使您需要对简单 int32 中的所有数字求和,也不会溢出。

您将花费的时间大约为 O(n+n)

【讨论】:

  • 你的解决方案还是O(N^2)
  • @Ben 不,注意内部循环使用arr
  • 优化溢出问题只是一个想法。我还没有找到减少 n^n 的方法。
  • 存在 DP 算法,但这个建议更适合作为评论而不是答案。
  • @LuchianGrigore,也许你是对的。但是,虽然现在我怀疑算法比 n*n 存在得更快。不过,明天再考虑。有趣的任务。
猜你喜欢
  • 1970-01-01
  • 2013-05-12
  • 2015-12-22
  • 1970-01-01
  • 1970-01-01
  • 2012-07-31
  • 2012-10-16
  • 1970-01-01
  • 2017-07-05
相关资源
最近更新 更多