计算给定k个模和的子序列数

时间:2015-09-12 10:58:40

标签: algorithm

给定an整数的数组,计算有多少个子序列(非连续的)sum % k = 0

1 <= k < 100
1 <= n <= 10^6
1 <= a[i] <= 1000

可以轻松实现O(n^2)解决方案,但需要更快的O(n log n)O(n)方式。

3 个答案:

答案 0 :(得分:2)

这是subset sum问题。

一个简单的解决方案就是:

s = 0
dp[x] = how many subsequences we can build with sum x 
dp[0] = 1, 0 elsewhere
for i = 1 to n:
    s += a[i]
    for j = s down to a[i]:
        dp[j] = dp[j] + dp[j - a[i]]

然后,您只需返回所有dp[x]的总和,即x % k == 0。但这具有很高的复杂性:大约O(n*S),其中S是所有元素的总和。 dp数组也必须具有S大小,您可能甚至无法为约束声明这些大小。

更好的解决方案是不首先迭代大于或等于k的和。为此,我们将使用2个dp数组:

dp1, dp2 = arrays of size k
dp1[0] = dp2[0] = 1, 0 elsewhere
for i = 1 to n:
    mod_elem = a[i] % k
    for j = 0 to k - 1:
        dp2[j] = dp2[j] + dp1[(j - mod_elem + k) % k]

    copy dp2 into dp1

return dp1[0]

其复杂性为O(n*k),并且是此问题的最佳选择。

答案 1 :(得分:0)

有一个O(n + k^2 lg n) - 时间算法。计算输入数组mod c(0), c(1), ..., c(k-1)的直方图k(即,c(r) mod rk个元素。然后计算

  k-1
product (1 + x^r)^c(r) mod (1 - x^k)
  r=0

如下所示,其中简化多项式的常数项是答案。

我们不是用快速取幂方法评估每个因子然后相乘,而是将内容翻出来。如果所有c(r)都为零,则答案为1。否则,递归评估

      k-1
P = product (1 + x^r)^(floor(c(r)/2)) mod (1 - x^k).
      r=0

然后计算

      k-1
Q = product (1 + x^r)^(c(r) - 2 floor(c(r)/2)) mod (1 - x^k),
      r=0

通过利用因子的稀疏性来计算后者O(k^2)。结果是P^2 Q mod (1 - x^k),通过天真卷积在时间O(k^2)中计算。

答案 2 :(得分:0)

遍历a并计算a[i] mod k;应该k这样的数量。

k, 2*k, 3*k...etc.的不同分区进行递归和记忆,其中部分小于或等于k,并添加相应计数的产品。

例如,如果k10,则部分分区为1+2+71+2+3+4;但是在记忆时,我们只需要计算一次数组k中有多少对产生(1 + 2)

例如,k = 5, a = {1,4,2,3,5,6}

counts of a[i] mod k: {1,2,1,1,1}

products of distinct partitions of k:
  5   => 1
  4,1 => 2
  3,2 => 1

products of distinct partitions of 2 * k with parts <= k:
  5,4,1   => 2
  5,3,2   => 1
  4,1,3,2 => 2

products of distinct partitions of 3 * k with parts <= k:
  5,4,1,3,2 => 2

answer = 11

  {1,4} {4,6} {2,3} {5}
  {1,4,2,3} {1,4,5} {4,6,2,3} {4,6,5} {2,3,5}
  {1,4,2,3,5} {4,6,2,3,5}