查找数组的子数组,其数量除以给定数字

时间:2013-10-23 12:06:52

标签: c++ arrays algorithm math numerical-analysis

我遇到了一个算法问题。请为我提出一些针对以下问题的有效算法。

问题

查找其数量可以被给定数字整除的子数组。

我的工作

我做了一个算法,其复杂度为O(N ^ 2),这里,N =数组的大小。

我的代码

#include<stdio.h>

using namespace std;

 main() {
    int N;
    int P;
    int T;
    int val;
    long long int count = 0;
    long long int answer = 0;
    scanf("%d", &T);
    //T = 20;

    for(int k = 1; k <= T; k++) {
        scanf("%d", &N);
        scanf("%d", &P);
        count = 0;
        answer = 0;
        for(int i = 0; i < N; i++) {
            scanf("%d", &val);
            count += val;
            workingArray[i] = count;
        }

        for(int length = 1; length <= N; length++) {
            for(int start = 0; start <= (N-length); start++) {
                if( start == 0 ) {
                    if(workingArray[start+length-1]%P == 0) answer++;
                }
                else if( (workingArray[start+length-1] - workingArray[start-1])%P == 0) answer++;
            }
        }

        printf("Case #%d\n%lld\n", k, answer);
    }
    return 0;
 }

2 个答案:

答案 0 :(得分:23)

对于给定的数字X ...

基本理念:(非正式的证明)

如果[a, b]范围内的数字总和可以被X整除,那么:

(∑i=1 to a-1input[i]) % X = (∑i=1 to binput[i]) % X

用较少的数学术语:

the sum from the first element to b = the sum from the first element to a
                                    + the sum of the elements between the two

所以:

the sum of the elements between the two = the sum from the first element to b
                                        - the sum from the first element to a

然后,如果右边的那些总和在除以X时具有相同的余数,则剩余部分将抵消,并且两者之间的元素之和将被X整除。详细说明:

C = the sum of the elements between the two
B = the sum from the first element to b
A = the sum from the first element to a

现在,对于某些整数BPX + Q,我们可以将A转换为RX + SP形式转换为Q形式, RS0 <= Q, S < X。在这里,根据定义,QS将是BA除以X的剩余部分。

然后我们有:

C = (PX + Q) - (RX + S)
C = PX + Q - RX - S
C = PX - RX + Q - S
C = (P-R)X + Q - S

显然(P-R)X可以被X整除(结果只是(P-R))。现在我们只需要Q - S可以X整除,但是,自0 <= Q, S < X起,它们就必须相等。

示例:

允许B = 13A = 7X = 3

此处B % X = 1A % X = 1

我们可以将B重写为4*3 + 1,将A重写为2*3 + 1

然后C = 4*3 + 1 - 2*3 - 1 = 2*3,可以被3整除。

高级方法:

构造一个key -> value的哈希映射,其中每个值表示从数组的开头开始的多少种方式,最终在某个给定的位置加起来sum mod X = key(参见“Mod 3”行和下面示例中的地图值。)

现在,基于上面的逻辑,我们知道如果两个子阵列从头开始并分别在位置ab结束,两者都具有相同的sum mod X,则子阵列{ {1}}将被[a, b]整除。

因此,散列图中的每个值都代表一组可能的起点和终点的大小,它们将为我们提供一个可被X整除的子阵列(任何点都可以是起点或终点)。 / p>

选择这些起点和终点的可能方式的数量简单为
value choose 2 = value!/(2*(value-2)!)(如果值为1则为0)。

因此,我们计算哈希映射中的每个值,并将它们全部添加,以获得可被X整除的子数组的数量。

<强>算法:

构造一个哈希映射,它将存储迄今为止所有数字的累积和X映射到剩余值出现的频率计数(在预期的mod X中构建)。

O(n)的值增加1 - 这对应于数组的开头。

将计数初始化为0。

对于散列图中的每个值,将0添加到计数中。

计数是所需的值。

运行时间:

预计value!/(2*(value-2)!)

示例:

O(n)

子阵列将是:

Input:    0  5  3  8  2  1
X = 3

Sum:   0  0  5  8 16 18 19
Mod 3: 0  0  2  2  1  0  1

Map:
  0 -> 3
  1 -> 2
  2 -> 2

Count = 3! / 2*(3-2)! = 3  +
        2! / 2*(2-2)! = 1  +
        2! / 2*(2-2)! = 1
      = 5

答案 1 :(得分:2)

我可能有一个更简单的解决方案。在O(n)时间和O(n + k)空间。其中n是数组的大小&amp; k是我们用它来检查可分性的数字。

将数组视为A [n],数字为K

  1. 创建另一个数组SUM_TILL_NOW [n]。
  2. 对于每个A [i]填充SUM_TILL_NOW [i] = SUM_TILL_NOW [i-1] + A [i]%K; (SUM_TILL_NOW [0] = A [0])
  3. 在这个新数组中找到两个相等的数字。
  4. 为此,创建一个大小为K的新数组CHECK [。

    迭代SUM_TILL_NOW数组并检查是否设置了CHECK [SUM_TILL_NOW [i]]。

    如果没有设置为i。

    else CHECK [SUM_TILL_NOW [i]],i是和可以被K整除的子集。

    下面是相同的c ++函数。

    #include <iostream>
    #include <string.h>
    
    using namespace std;
    
    void printrange(int* A, int N, int K)
    {
        int STN[N], C[K];
        memset(C, -1, K*sizeof(int));
        int i;
        int sum=A[0];
        STN[0]= (A[0]%K);
        for (i= 1; i< N; i++)
        {
            sum+= A[i];
            STN[i]= sum%K;
        }
        for(i=0; i< N; i++)
        {
            if(C[STN[i]] == -1)
                C[STN[i]] =i;
            else
            {
                cout<< C[STN[i]]+1 <<" "<< i;
                break;
            }
        }
    }
    
    int main()
    {
        int A[]= {6, 9, 2, 1, 8, 6, 2, 5};
        printrange(A, sizeof(A)/sizeof(A[0]), 7);
        return 0;
    }