简体   繁体   English

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

[英]Find numbers of subarray of an array whose sum is divided by given number

I got stuck in one algorithm question. 我遇到了一个算法问题。 Please suggest me some efficient algorithm for the below problem. 请为我提出一些针对以下问题的有效算法。

Question is 问题是

Find numbers of subarrays whose sum is divisible by given number. 查找数量的子数组,其总和可以被给定的数字整除。

My work 我的工作

I made one algorithm, whose complexity is O(N^2), here, N = size of an array. 我做了一个算法,其复杂度为O(N ^ 2),这里,N =数组的大小。

My Code 我的守则

#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;
 }

For a given number X ... 对于给定的数字X ......

The basic idea: (with informal proof of correctness) 基本思路:(具有正确性的非正式证明)

If the sum of the numbers in the range [a, b] is divisible by X , then: 如果[a, b]范围内的数字之和可被X整除,则:

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

In less mathematical terms: 用较少的数学术语:

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

So: 所以:

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

Then, if those sums on the right both have the same remainder when divided by X , the remainders will cancel out and sum of the elements between the two will be divisible by X . 然后,如果右边的那些总和在除以X时都具有相同的余数,则剩余部分将抵消,并且两者之间的元素之和将被X整除。 An elaboration: 详细说明:

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

Now we can convert B to the form PX + Q and A to the form RX + S , for some integers P , Q , R and S , with 0 <= Q, S < X . 现在我们可以将B转换为形式PX + QA转换为RX + S形式,对于某些整数PQRS ,其中0 <= Q, S < X Here, by definition, Q and S would be the respective remainders of B and A being divided by X . 这里,根据定义, QS将是BA的各个余数除以X

Then we have: 然后我们有:

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

Clearly (PR)X is divisible by X (the result is simply (PR) ). 显然, (PR)X可以被X整除(结果就是(PR) )。 Now we just need Q - S to be divisible by X , but, since 0 <= Q, S < X , they'll need to be equal. 现在我们只需要Q - S可以被X整除,但是,因为0 <= Q, S < X ,它们需要相等。

Example: 例:

Let B = 13 , A = 7 , X = 3 . B = 13A = 7X = 3

Here B % X = 1 and A % X = 1 . 这里B % X = 1A % X = 1

We can rewrite B as 4*3 + 1 and A as 2*3 + 1 . 我们可以将B重写为4*3 + 1A重写为2*3 + 1

Then C = 4*3 + 1 - 2*3 - 1 = 2*3 , which is divisible by 3 . 然后C = 4*3 + 1 - 2*3 - 1 = 2*3 ,可被3整除。

High-level approach: 高级方法:

Construct a hash-map of key -> value , where each value represents how many ways you can start from the beginning of the array and end up at some given position which adds up to sum mod X = key (see the "Mod 3" line and the map values in the example below). 构造key -> value的哈希映射,其中每个值表示从数组开头可以开始的方式,并在某个给定位置结束,加起来为sum mod X = key (参见“Mod 3”)行和下面示例中的地图值)。

Now, based on the logic above, we know that if two subarrays starting from the beginning and ending at positions a and b respectively, both having the same sum mod X , subarray [a, b] will be divisible by X . 现在,基于上面的逻辑,我们知道如果两个子阵列从开始开始并分别在位置ab结束,两者都具有相同的sum mod X ,则子阵列[a, b]将被X整除。

So each value in the hash-map represents the size of a set of possible starting and ending points which will give us a subarray divisible by X (any point can be either a starting or ending point). 因此,散列图中的每个值表示一组可能的起点和终点的大小,这将给我们一个可被X整除的子阵列(任何点都可以是起点或终点)。

The number of possible ways to choose these starting and ending points is simply 选择这些起点和终点的可能方式很简单
value choose 2 = value!/(2*(value-2)!) (or 0 if value is 1). value choose 2 = value!/(2*(value-2)!) (如果值为1则为0)。

So we calculate that for each value in the hash-map and add them all up to get the number of subarrays divisible by X . 因此,我们计算哈希映射中的每个值,并将它们全部加起来以获得可被X整除的子数组的数量。

Algorithm: 算法:

Construct a hash-map which will store the cumulative sum of all the numbers thus far mod X mapped to the count of how often that remainder value appears (constructed in expected O(n) ). 构造一个哈希图,其将存储迄今为止的所有号码的累积和mod X映射到的如何经常出现该剩余值的计数(在预期构造O(n)

Increase 0 's value by one - this corresponds to the start of the array. 0的值增加1 - 这对应于数组的开始。

Initialise a count to 0. 将计数初始化为0。

For each value in the hash-map, add value!/(2*(value-2)!) to the count. 对于散列映射中的每个值,将value!/(2*(value-2)!)到计数中。

The count is the desired value. 计数是期望值。

Running time: 运行时间:

Expected O(n) . 预期O(n)

Example: 例:

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

The subarrays will be: 子阵列将是:

0  5  3  8  2  1
-                     0                 =  0 % 3 = 0
-------------         0 + 5 + 3 + 8 + 2 = 18 % 3 = 0
   ----------         5 + 3 + 8 + 2     = 18 % 3 = 0
      -               3                 =  3 % 3 = 0
            ----      2 + 1             =  3 % 3 = 0

I may have an easier solution. 我可能有一个更简单的解决方案 in O(n) time and O(n+k) space. 在O(n)时间和O(n + k)空间。 where n is the size of array & k is the number we are checking the divisibility with. 其中n是数组的大小,k是我们检查可分性的数字。

Consider the array as A[n] and the number is K 将数组视为A [n],数字为K.

  1. create another array SUM_TILL_NOW[n]. 创建另一个数组SUM_TILL_NOW [n]。
  2. for each A[i] fill SUM_TILL_NOW [i]= SUM_TILL_NOW[i-1]+A[i] %K; 对于每个A [i]填充SUM_TILL_NOW [i] = SUM_TILL_NOW [i-1] + A [i]%K; (SUM_TILL_NOW[0]= A[0]) (SUM_TILL_NOW [0] = A [0])
  3. find two numbers which are equal in this new Array. 在这个新数组中找到两个相等的数字。

To do that create a new array CHECK[] of size K. 为此,创建一个大小为K的新数组CHECK []。

Iterate over the SUM_TILL_NOW array and check if CHECK[SUM_TILL_NOW[i]] set. 迭代SUM_TILL_NOW数组并检查是否设置了CHECK [SUM_TILL_NOW [i]]。

If not set it to i. 如果没有设置为i。

else CHECK[SUM_TILL_NOW[i]], i is the subset where the sum is divisible by K. 否则检查[SUM_TILL_NOW [i]],i是总和可被K整除的子集。

Below is a c++ function of the same. 下面是一个相同的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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM