当需要快速查询数据的记录时:
- 手动按某规则为数据建立索引,利用数组维护数据
- 若1中需要开大数组爆内存,利用官方库的Hash集合,可以将最好情况下的时间复杂度优化到O(1)避免T和M。
1. LC 2036 最大好子数组和
VP双周赛123T3。一开始卡住了。一直想在Hash表里存每种数字出现的索引,查询nums[i]±k的所有可能索引,前缀和相减得到子数组和维护最大值。最后不出意外的T了。既然要找之前的最小前缀和,为啥不在Hash表里直接维护每个数字对应的最小前缀和呢?改完A了。
import java.util.HashMap;
class Solution {
public long maximumSubarraySum(int[] nums, int k) {
HashMap<Integer, Long> m = new HashMap<>();
int n = nums.length;
long[] prefix = new long[n+1];
long ans = Long.MIN_VALUE;
long sum = 0;
for (int i = 0; i < n; i++) {
sum += nums[i];
prefix[i+1] = sum;
Long nk = m.get(nums[i] - k);
Long pk = m.get(nums[i] + k);
if(nk!=null){
ans = Math.max(ans,sum-nk);
}
if(pk!=null){
ans = Math.max(ans,sum-pk);
}
Long pre = m.get(nums[i]);
if(pre==null){
m.put(nums[i],prefix[i]);
}else{
m.put(nums[i],Math.min(prefix[i],pre));
}
}
return ans==Long.MIN_VALUE?0:ans;
}
}
注意子数组是闭区间,前缀和数组里加个哨兵即可。然后这道题实际上不需要前缀和数组,滚动一个前缀和就可以了,闭区间滞后一个元素即可。
import java.util.HashMap;
class Solution {
public long maximumSubarraySum(int[] nums, int k) {
HashMap<Integer, Long> m = new HashMap<>();
int n = nums.length;
long ans = Long.MIN_VALUE;
long sum = 0;
for (int num : nums) {
Long nk = m.get(num - k);
Long pk = m.get(num + k);
if (nk != null) {
ans = Math.max(ans, sum + num - nk);
}
if (pk != null) {
ans = Math.max(ans, sum + num - pk);
}
Long pre = m.get(num);
if (pre == null) {
m.put(num, sum);
} else {
m.put(num, Math.min(sum, pre));
}
sum += num;
}
return ans==Long.MIN_VALUE?0:ans;
}
}
2. LC 3164 优质数对的总数Ⅱ
- 先筛选nums1中能被k整除的数,把x//k加入到数组中
- 哈希表m再统计nums2各个数字出现的频率
- 对于通过筛选的nums1中出现的数v,枚举[1:sqrt(v)],查看是否是因子d
- 不是因子跳过
- 是因子
- 是平方根,加 m[d]
- 不是平方根,加 m[d] + m[v//d]
from typing import List
from collections import defaultdict
import math
class Solution:
def numberOfPairs(self, nums1: List[int], nums2: List[int], k: int) -> int:
arr = []
for x in nums1:
if x%k==0:
arr.append(x//k)
m = defaultdict(int)
for x in nums2:
m[x] += 1
res = 0
for x in arr:
for i in range(1,int(math.sqrt(x))+1):
if x%i == 0:
res += m[i] + (m[x//i] if i*i!=x else 0)
return res