此次带来的题目是P3131 [USACO16JAN] Subsequences Summing to Sevens S,很适合作为哈希表,前缀和的初步认识。从题意得,需要知道可以拍摄的最大奶牛组大小,那么其实不需要重复计算,每次记录前面数的和,之后再在前面数的和上直接加上新数就行了,然后分别从每个数开始循环一次,就覆盖了全部的组,在其中找到最长的就行了。那么按照这个想法,第一版的代码就出来了。
#include <bits/stdc++.h>
using namespace std;
int main (){
int n,max=0;
cin>>n;
vector<long long> vec(n);
for(int i=0;i<n;i++){
cin>>vec[i];
vec[i]=vec[i]%7;
}
long long sum;
for(int i=0;i<n;i++){
sum=0;
for(int j=i;j<n;j++){
sum+=vec[j];
if(sum%7==0&&j-i+1>max){
max=j-i+1;
}
}
}
cout<<max;
return 0;
}
这个代码逻辑没有任何问题,但在提交的时候,有的测试样例却超时了。可以看一下算法的时间复杂度,有一个双层for循环,那么时间复杂度就是o(n*2)。既然时间超了,那么就需要降低时间复杂度,按照原先的想法好像不行。那么此时就可以改变一下思路,假如有这么一个数组,他记录的是前n个数的和再mod7,就和我们之前做的差不多。但是我们再来一个表,记录一下不同的余数在数组中第一次出现的位置,然后再在数组中找到相同余数时,把他的位置减去数组的位置,因为会记录前0个数的和,所以他们相减也就是一个组的和。那么为什么这个组满足呢,因为前n个数的和的余数和前m个数的和的余数相同,那么说明他们之间的数的和的余数一定是0,而题目就是要找这样的组,只要记录这个组的大小就行了,按照这个思路就有了如下的代码。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<long long> vec(n);
for (int i = 0; i < n; i++) {
cin >> vec[i];
}
vector<int> prefix_mod(n + 1, 0);
for (int i = 1; i <= n; i++) {
prefix_mod[i] = (prefix_mod[i - 1] + vec[i - 1]) % 7;
}
unordered_map<int, int> first_occurrence;
int max_len = 0;
for (int i = 0; i <= n; i++) {
int mod = prefix_mod[i];
if (first_occurrence.find(mod) == first_occurrence.end()) {
first_occurrence[mod] = i;
} else {
max_len = max(max_len, i - first_occurrence[mod]);
}
}
cout << max_len << endl;
return 0;
}
在这其中,运用了一个非常独特的数据结构哈希表,哈希表就是一个既可以存储键也可以存储值的表,那么你可以根据对应的键来存储对应的值。在这道题当中,就可以对相应的余数存储相应的位置信息,这样就非常方便。另外,在哈希表中有几个常用操作。下面列出了相应操作的代码和时间复杂度。
| 插入/修改 | map[key] = value; | O(1) |
| 查找 | if (map.find(key) != map.end()) | O(1) |
| 删除 | map.erase(key); | O(1) |
| 遍历 | for (auto& pair : map) | O(n) |
在高频查询、去重、统计频率等场景,掌握它的基本操作,就能大幅提升代码效率。