我在接受采访时遇到了以下问题,尽管我提供了一个有效的实施方案,但效率还不够高。
阵列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}
答案 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
现在,对于某些整数B
,PX + Q
,我们可以将A
转换为RX + S
和P
形式转换为Q
形式, R
和S
,0 <= Q, S < X
。在这里,根据定义,Q
和S
将是B
和A
除以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 = 13
,A = 7
,X = 3
。
此处B % X = 1
和A % 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,虽然非常整洁!我只是想补充一些意见。
res += storedArray[0];
来修复它。{4,5,0,-2,-3,1}; {5}; {5,0}; {5,0,-2,-3}; {0}; {0,-2,-3}; {-2,-3}
cache[0]++;
取决于语言,如果使用C ++,则需要它,但java [link]不需要。代码:
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);
}
}