LeetCode: 523. Continuous Subarray Sum
这道题的简单版是求子数列的和的最大值,一般的做法是做动态规划,或者用两个指针指示子数列前后,和一个int来存当前已知的最大值,最后遍历一次数组就能解决了。这次变得困难了一些。是求是否存在一个子数列的和是给定非负整数k的整数倍。毫无思路,后来看了一下讨论。感觉清晰多了,整理如下:
暴力破解法
时间复杂度是O(n^2),空间复杂度是O(n)。运行时间大概会用到将近160ms(Ac边缘),很暴力,就是算出所有可能,然后一个个可能去判断是否是k的倍数。最后有个特殊边界是0,0和k=0的情况。
public boolean checkSubarraySum(int[] nums, int k) {
if (nums.length < 2) return false;
if (k == 0) {
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] == 0 && nums[i + 1] == 0) return true;
}
return false;
}
int[] dp = Arrays.copyOf(nums, nums.length);
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < nums.length - i; j++) {
dp[j] += nums[j + i];
if (dp[j] % k == 0) return true;
}
}
return false;
}
很巧妙的方法
只需要历遍数组一次,计算runningSum的大小,一旦出现两个一样的runningSum而且中间相隔1个以上的数字,那么之间的子数列就是一个和为k的整数倍的数列。时间复杂度是O(n),为了节省搜索的时间,用了C++STL中的哈希表实现unordered_map(在c++11前是hash_map,c++11推荐使用unordered_map)这样空间复杂度是O(k),查找的时间复杂度是O(1)。各种省。最后跑了16ms,战胜100%cpp提交答案……
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k)
{
if (nums.size() < 2)
return false;
int runningSum = 0;
unordered_map<int, int> quickFind;
quickFind[0] = -1;
for(int i = 0; i < nums.size(); i++)
{
runningSum += nums[i];
if(k != 0)
runningSum %= k;
std::unordered_map<int, int>::iterator it;
it = quickFind.find(runningSum);
if(it == quickFind.end())
quickFind[runningSum] = i;
else if(i - it->second > 1)
return true;
}
return false;
}
};
要注意的是,hash_map的实现其实是分三部分,包括一个vector,一个list和一个pair,其中vector用于保存桶,list用于进行冲突处理,pair用于保存key->value结构。哈希表的一个重要问题就是如何解决映射冲突的问题。不知道unordered_map的实现是不是也类似,不过原理肯定也是差不多的,一个数据结构保存桶,一个数据结构处理冲突,一个数据结构来保存。
另外要熟习unordered_map的用法,其实和map用法差不多,他们的insert都是要insert一个pair的,这需要我们做一个std::make_pair,这样代码上看上去很复杂,其实在插入的时候,直接可以当数组用。即map[ke]=value。更加方便
还有用unordered_set的,其实效果一样。