30

我在一次采访中遇到了以下问题,尽管我给出了一个可行的实现,但它的效率还不够高。

数组 A 的切片是满足 0 ≤ P ≤ Q < N 的任意整数对 (P, Q)。如果数字 A[P] + A[P +1] + ... + A[Q−1] + A[Q] 可以被 K 整除。

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

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

我一直在想,但我真的不知道如何在 O(max(N, K)) 中做到这一点。

它可能是子集和问题的变体,但我不知道如何计算每个子数组。

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

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}
4

7 回答 7

41

由于您只对可被 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 的单元格结束的切片数,这个数字是x ( x - 1 ) / 2。为了解决边缘问题,我们添加了一个值为 0 的单元格。

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

于 2013-05-17T10:17:56.410 回答
4

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

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

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

public static int countSubarrays(int[] nums, int k) {
    int[] cache = new int[k];
    cache[0]++;
    int s = 0, counter = 0;
    for (int i = 0; i < nums.length; i++) {
        s = ((s + nums[i]) % k + k) % k;
        counter += cache[s];
        cache[s]++;
    }

    return counter;
}
于 2016-09-21T14:57:12.783 回答
1

例子 :-

输入数组

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

这是程序

公共静态 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;
    }
于 2016-12-11T03:53:29.120 回答
0
    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;
    }
于 2013-06-02T10:01:30.927 回答
0

感谢您的解决方案,@damluar,但它非常整洁!我只是想添加一些评论。

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

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

参考链接

  1. 初始化cache[0]++;取决于语言,如果使用 C++,则需要,但对于 java [链接] 则不需要。

代码:

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; 
}
}
于 2017-11-04T22:50:09.643 回答
-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();


    }
于 2013-06-25T21:24:57.287 回答
-3
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);
    }
}
于 2013-05-17T17:59:26.963 回答