查找总和除以给定数字的数组的子数组的数量
Find numbers of subarray of an array whose sum is divided by given number
我陷入了一个算法问题。请为我建议一些针对以下问题的有效算法。
问题是
查找其总和可被给定数字整除的子数组的数量。
我的工作
我做了一个算法,其复杂度是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 #%dn%lldn", k, answer);
}
return 0;
}
对于给定的数量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
现在我们可以将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
整除。
高级方法:
构造一个 key -> value
的哈希映射,其中每个值表示您可以从数组的开头开始到在某个给定位置结束的几种方式,加起来为 sum mod X = key
(请参阅"Mod 3"行和下面的示例中的映射值)。
现在,基于上面的逻辑,我们知道如果两个子数组分别从位置开始和结束于位置 a
和 b
,两者都具有相同的sum mod X
,子数组[a, b]
将被 X
整除。
因此,哈希映射中的每个值都表示一组可能的起点和终点的大小,这将为我们提供一个可被X
整除的子数组(任何点可以是起点或终点)。
选择这些起点和终点的可能方法的数量很简单
value choose 2 = value!/(2*(value-2)!)
(如果值为 1,则为 0)。
因此,我们计算哈希映射中的每个值,并将它们全部相加以得到可被X
整除的子数组的数量。
算法:
构造一个哈希映射,它将存储到目前为止所有数字的累积总和,mod X
映射到该余数值出现的频率(以预期O(n)
构造)。
将 0
的值增加 1 - 这对应于数组的开头。
将计数初始化为 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
子数组将是:
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
我可能有一个更简单的解决方案。 在 O(n) 时间和 O(n+k) 空间中。 其中n是数组的大小,k是我们正在检查可除性的数字。
将数组视为 A[n],数字为 K
- 创建另一个数组 SUM_TILL_NOW[n]。
- 对于每个 A[i] 填充SUM_TILL_NOW [i]= SUM_TILL_NOW[i-1]+A[i] %K;(SUM_TILL_NOW[0]= A[0])
- 在这个新数组中找到两个相等的数字。
为此,创建一个大小为 K 的新数组 CHECK[]。
遍历SUM_TILL_NOW数组并检查是否设置了 CHECK[SUM_TILL_NOW[i]]。
如果未设置为 i。
否则 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;
}
- 在将数字随机生成为数组期间从内存输出随机数的数组
- 如何计算数组中元素的位数?(不是数组的长度),并计算其数字的总和
- 数组/c++中的大量数字
- 遍历并行数组以确定C++中的最大数字
- 整数区间(或 int 数组)中每个数字的出现次数
- 如何在 C++ 中将文件中的逗号分隔数字读取到数组中?
- 将随机生成的数字添加到数组 + 对这些数组求平均值
- 遇到此问题时遇到困难:允许用户输入数组的值并使用 for,而循环也输出输入的最大数字
- 获取 2D 数组 c++ 中的所有数字对
- 为什么使用数组元素查找最大数字的程序不起作用?
- 如何从向量或数组中选择最常见的数字?(前五名)C++
- 如何从保存在 Java 中C++的字节数组中读取数字?
- 以十为基数的数字到布尔数组,该数组要求二进制/基数为 2 的数字
- C++ 检查结果数组中有多少次数字
- 如何在不使用 C++ 中的数组或函数的情况下查找 N 位数字的所有排列
- 如何在C++中有效地将数字值重新分配给字符数组
- 查找数组中的最小数字
- 查找数组中重复(重复)数字的索引
- 我必须找到给定数组中所有数字的周期,就像有很多解决方案,但数组的大小是 10^5
- 是否可以将数组数字作为函数来执行?