题意
给一个由非负正数组成的数组和一个数k,要求在数组中找一段长度 ≥2 的序列使其和是k的整数倍。
思路
算法1
O(n2) 时间, O(n) 空间
维护数组的前缀和
s
,对于当前
注意 k=0 的情况。
算法2
如果要求和刚好为k的话,我们对于当前和
si
,只需要知道之前是否存在
sj=si−k
即可,于是可以用unordered_map
O(1)
的去查找。
既然是k的整数倍,我们可以利用%的性质:
x%p=a 等价于 x=pn+a(1)
y%p=a 等价于 y=pm+a(2)
(2) 和 (1) 相减既有: y−x=p(m−n) ,即 y−x 是 p <script type="math/tex" id="MathJax-Element-33">p</script>的整数倍。
所以,我们不维护前缀和而是前缀和%k之后的值x,并且用map保存每个x出现的最早位置,当再次出现x的时候,并且长度大于1,那么就是我们的结果。
代码
algorithm 1
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
vector<int> sum(nums.size(), 0);
if (nums.size()) {
for (int i = 0; i < nums.size(); i++) sum[i] = (i ? sum[i - 1] : 0) + nums[i];
for (int i = 0; i < nums.size(); i++) {
if (k == 0) {
if (i && sum[i] == 0) return true;
} else {
if (sum[i] % k == 0 && i) return true;
}
for (int j = 0; j < i - 1; j++) {
int x = sum[i] - sum[j];
if (k == 0) {
if (x == 0) return true;
} else {
if (x % k == 0) return true;
}
}
}
}
return false;
}
};
algorithm 2
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
unordered_map<int, int> pos;
if (nums.size()) {
int sum = 0; pos[0] = -1;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
if (k) sum %= k;
if (pos.count(sum)) {
int x = pos[sum];
if (i - x > 1) return true;
} else {
pos[sum] = i;
}
}
}
return false;
}
};