可被k整除的子数组的数量

时间:2013-05-17 09:43:10

标签: arrays algorithm subset-sum

我在接受采访时遇到了以下问题,尽管我提供了一个有效的实施方案,但效率还不够高。

  

阵列A的切片是任何一对整数(P,Q),使得0≤P≤Q   < N.如果数字A [P] +,则数组A的切片(P,Q)可被K整除   A [P + 1] + ... + A [Q-1] + A [Q]可被K整除。

我被要求写的函数必须返回被K整除的切片数。预期的时间复杂度为O(max(N,K)),空间复杂度为O(K)。

我的解决方案是最简单的一个循环,并检查每个切片:O(n ^ 2)

我一直在想,但我真的无法弄清楚如何在O(max(N,K))中做到这一点。

它可能是subset sum problem的变体,但我不知道如何计算每个子阵列。

编辑:数组中的元素可能是否定的。这是一个例子:

A = {4, 5, 0, -2, -3, 1}, K = 5

Function must return 7, because there are 7 subarrays which sums are divisible by 5
{4, 5, 0, -2, -3, 1}
{5}
{5, 0}
{5, 0, -2, -3}
{0}
{0, -2, -3}
{-2, -3}

8 个答案:

答案 0 :(得分:28)

由于您只对可被K整除的数字感兴趣,因此您可以以K为模进行所有计算。 考虑累积和数组S,使S[i] = S[0] + S[1] + ... + S[i]。然后(P,Q)是可被K iff S[P] = S[Q]整除的切片(记住我们以K为模进行所有计算)。所以你只需计算[0,...,K-1]的每个可能值,它在S中出现的次数。

这是一些伪代码:

B = new array( K )
B[0]++
s = 0
for i = 0 to N - 1
  s = ( s + A[i] ) % K
  B[s]++
ans = 0
for i = 0 to K - 1
  ans = ans + B[i] * ( B[i] - 1 ) / 2

一旦你知道它们是S中具有值i的x单元,你想要计算具有值i的单元格中的起始切片的数量,并在具有值i的单元格中结束,这个数字是{{1} }。为了解决边缘问题,我们添加一个值为0的单元格。

x ( x - 1 ) / 2代表什么:假设我们的数组是[4,5,0],频率为4,因为前缀sum是x,在这种情况下是3。现在我们可以从x的值得出结论,至少有x-1个数字可以被k整除或者mod k等于0.现在这些x-1数字中可能的总数是1 + 2 + 3 ...... +(x - 1)是x ( x - 1 ) / 2。 (从1到N求和的标准公式,其中N代表(x - 1)。

答案 1 :(得分:6)

对于给定的数字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整除。

高级方法:

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

0的值增加1 - 这对应于数组的开头。

将计数初始化为0。

浏览哈希图并将nC2(= value!/(2*(value-2)!))添加到计数中。我们在这里选择的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
  2 -> 2
  1 -> 2

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

子阵列将是:

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

答案 2 :(得分:1)

    private int GetSubArraysCount(int[] A, int K)
    {
        int N = A.Length;
        int[] B = new int[K];
        for (int i = 0; i < B.Length; i++)
        {
            B[i] = 0;
        }
        B[0]++;
        int s = 0;
        for (int i = 0; i < N; i++)
        {
            s = (s + A[i]) % K;
            while (s < 0)
            {
                s += K;
            }
            B[s]++;
        }
        int ans = 0;
        for (int i = 0; i <= K - 1; i++)
        {
            ans += B[i] * (B[i] - 1) / 2;
        }
        return ans;
    }

答案 3 :(得分:1)

static void Main(string[] args)
    {
        int[] A = new int[] { 4, 5, 0, -2, -3, 1 };
        int sum = 0;
        int i, j;
        int count = 0;
        for (i = 0; i < A.Length; i++)
        {
            for (j = 0; j < A.Length; j++)
            {
                if (j + i < 6)
                    sum += A[j + i];
                if ((sum % 5) == 0)
                    count++;

            }
            sum = 0;
        }
        Console.WriteLine(count);
        Console.ReadLine();


    }

答案 4 :(得分:0)

这是@Thomash提出的解决方案的Java实现。

第二个循环不是必需的,因为我们可以通过当前值直接增加答案然后递增它。

为避免负数组索引,我们还必须调整模块计算。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
	<li id="hoverer" class="tab-link" data-tab="ideas_by_stage" style="position:relative">Hover here...</li>

	<div id="test" style="display:none;position:absolute;z-index:999999;border:1px solid grey;width:200px;padding:4px 10px;left:170px;background-color:white;">

	    <div class="btn"><a href="">Option 1</a></div>
	    <div class="btn">Option 2</div>
	    <div class="btn">Option 3</div>
	    <div class="btn">Option 4</div>

	</div>

答案 5 :(得分:0)

示例: -

输入数组

int [] nums = {4,3,1,2,1,5,2};

K为3

连续总和

4,7,8,10,11,16,18

将连续和数组除以3

1,1,2,1,2,1,0

所以我们有四个1,两个2,一个0

总计数为(4 * 3)/ 2 +(2 * 1)/ 2 +(2 * 1)/ 2 = 8

(4 * 3)/ 2来自四个中的任意两个1,即nC2 = n(n-1)/ 2

这是程序

public static long countSubArrayDivByK(int k,int [] nums){

    Map<Integer, Integer> modulusCountMap = new HashMap<Integer, Integer>();
    int [] consecSum = new int[nums.length];
    consecSum[0]=nums[0];

    for(int i=1;i<nums.length;i++){
        consecSum[i]= consecSum[i-1] +nums[i];
    }

    for(int i=0;i<nums.length;i++){
        consecSum[i]= consecSum[i]%k;

            if(consecSum[i]==0 && modulusCountMap.get(consecSum[i])==null){
                modulusCountMap.put(consecSum[i], 2);
            }else{
                modulusCountMap.put(consecSum[i], modulusCountMap.get(consecSum[i])==null ? 1 : modulusCountMap.get(consecSum[i])+1);
            }

    }

    int count = 0;

    for (Integer val : modulusCountMap.values()) {
        count = count +  (val*(val-1))/2;
    }

    return count;
}

上述优化版

static long customOptimizedCountSubArrayDivByK(int k, int[] nums) {

        Map<Integer, Integer> modulusCountMap = new HashMap<Integer, Integer>();
        int [] quotient = new int[nums.length];
        quotient[0]=nums[0]%3;



        if(quotient[0]==0){
            modulusCountMap.put(quotient[0], 2);
        }else{
            modulusCountMap.put(quotient[0], 1);
        }


        for(int i=1;i<nums.length;i++){
            quotient[i]= (quotient[i-1] + nums[i])%3;


                if(quotient[i]==0 && modulusCountMap.get(quotient[i])==null){
                    modulusCountMap.put(quotient[i], 2);
                }else{
                    modulusCountMap.put(quotient[i], modulusCountMap.get(quotient[i])==null ? 1 : modulusCountMap.get(quotient[i])+1);
                }

        }

        int count = 0;

        for (Integer val : modulusCountMap.values()) {
            count = count +  (val*(val-1))/2;
        }

        return count;
    }

答案 6 :(得分:0)

感谢your solution,@damluar,虽然非常整洁!我只是想补充一些意见。

  1. 输出应为7,而不是6作为输出。因为我们有7个可被k整除的子阵列,如下所示,添加res += storedArray[0];来修复它。
  2.   

    {4,5,0,-2,-3,1}; {5}; {5,0}; {5,0,-2,-3}; {0}; {0,-2,-3}; {-2,-3}

    Ref link

    1. 初始化cache[0]++;取决于语言,如果使用C ++,则需要它,但java [link]不需要。
    2. 代码:

      public class HelloWorld{
      
      public static void main(String []args){
          int [] A = new int[] {4,5,0,-2,-3,1};
          int k = 5;
          int ans=0;
          System.out.println(countSubArray(A, k)); // output = 7
      
      }
      
      public static int countSubArray(int [] nums, int k){
          int [] storedArray = new int[k];
          int sum=0, res=0;
          for(int i=0; i<nums.length; i++){
              sum = (((sum + nums[i]) % k) + k) % k;
              res += storedArray[sum];
              storedArray[sum]++;
      
          }
          res += storedArray[0];
          return res; 
      }
      }
      

答案 7 :(得分:-2)

public class SubArrayDivisible 
{

    public static void main(String[] args) 
    {
        int[] A = {4, 5, 0, -2, -3, 1};
        SubArrayDivisible obj = new SubArrayDivisible();
        obj.getSubArrays(A,5);
    }

    private void getSubArrays(int[] A,int K)
    {
        int count = 0,s=0;
        for(int i=0;i<A.length;i++)
        {
            s = 0;
            for(int j = i;j<A.length;j++)
            {
                s = s+A[j];
                if((s%K) == 0)
                {
                    System.out.println("Value of S "+s);
                    count++;
                }

            }
        }
        System.out.println("Num of Sub-Array "+count);
    }
}