关于算法:数之和可被K整除的子数组数

number of subarrays where sum of numbers is divisible by K

给定一个数组,找出存在多少个这样的子序列(不需要是连续的),其中该子数组中的元素之和可被K整除。

我知道一种复杂度为2 ^ n的方法,如下所示。 就像找到所有i = [0,n]的nCi并验证和是否可以被K整除一样。
请提供伪代码,例如线性/二次或n ^ 3。

1
2
3
4
5
6
7
8
9
10
static int numways = 0;
void findNumOfSubArrays(int  [] arr,int index, int sum, int K) {
        if(index==arr.length) {
                if(sum%k==0) numways++;
        }
        else {
                findNumOfSubArrays(arr, index+1, sum, K);
                findNumOfSubArrays(arr, index+1, sum+arr[index], K);
        }
}


输入-长度为n的数组A,自然数为k。

算法:

  • 构造数组B:对于每个1 <= i <= n:B [i] =(A [i]模K)。

现在我们可以使用动态编程了:

我们定义D [i,j] =-B [i..n]的子数组的最大数目,其子元素的模数之和等于j。

1 <= i <= n。 0 <= j <= k-1。

D [n,0] =如果(b [n] == 0),2。否则,1。

如果j> 0:

D [n,j] =如果(B [n]模k)== j,则大于1,否则为0。

对于i

D [i,j] = max {D [i + 1,j],1 + D [i + 1,D [i + 1,(j-B [i] + k)模k)]]}。

  • 构造D。

  • 返回D [1,0]。

总运行时间:O(n * k)


从根本上说,如果K的范围和数组中数字的范围未知,那么我认为不可能在O(n ^ 3)甚至多项式时间内解决此问题。这是我的想法:

考虑以下情况:arr中的N个数字类似于

[1,2,4,8,16,32,...,2 ^(N-1)]

这样,arr的2 ^ N个"子数组"(不需要是连续的)的总和就是[0,2 ^ N]中的所有整数。

并问其中有多少可以被K整除,就等于问有多少整数在[0,2 ^ N)中可以被K整除。

我知道在上述情况下,答案可以像(2 ^ N-1)/ K(或其他)一样直接计算。但是,如果我们只是随机更改arr中的几个(可能是3?4?)个数字,以在完美连续整数范围[0,2 ^ N)中"挖一些随机孔",那么看起来计算答案而无需遍历[0,2 ^ N)中的几乎每个数字。

好吧,只是一些愚蠢的想法...可能是完全错误的。


使用辅助数组A

1)在输入时,将当前总计存储在相应的索引中(这在O(n)中执行):

1
2
3
4
5
6
7
int sum = 0;
for (int i = 0; i < n; i++)
{
    cin >> arr[i];
    sum += arr[i];
    A[i] = sum;
}

2)现在,

1
2
3
for (int i = 0; i < n; i++)
    for (int j = i; j < n; j++)
        check that (A[j] - A[i] + arr[i]) is divisible by k

你去了:O(n^2) ...