当需要快速查询数据的记录时:
- 手动按某规则为数据建立索引,利用数组维护数据
- 若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
3. LC 100358 找出有效子序列的最大长度Ⅱ
VP 周赛404 T3。写了个哈希表假解过了。
首先可以证明,对于一个合法子序列seq,seq[i]%k = seq[i+2]%k。
证明如下:
对于任意一个子序列中的相邻三元组(a,b,c),设其对k的余数为(p,q,r),下面证明p=r。
根据模数性质有:
因此根据题意可得:
第一种情况:
则p=r,得证。
第二种情况,采用反证法,假设p≠r。且有:
则因为:
那么:
不妨设:
其中:
则有:
这与r-p<k矛盾。
因此p=r,得证。
综上,结论中的性质成立。
实现上:
- 首先看出来相隔元素对k的余数相等
- 然后根据余数分组
- 然后两两匹配,构造最长子序列 优化的点:
- 自己和自己匹配就是当前组的长度 O(k)遍历一下即可
- 不同余数的组匹配,直接把第一个元素索引更小的组当作开始的组,否则结果必然不会更优
from collections import defaultdict
from typing import List
class Solution:
def maximumLength(self, nums: List[int], k: int) -> int:
ans = 2
m = defaultdict(list)
for i,x in enumerate(nums):
m[x%k].append(i)
for v in m.values():
ans = max(ans,len(v))
partitions = list(m.keys())
for i in range(len(partitions)):
for j in range(i+1,len(partitions)):
l1 = m[partitions[i]]
l2 = m[partitions[j]]
if l1[0] > l2[0]:
l1,l2 = l2,l1
p1,p2 = 0,0
stk = [l1[p1]]
flag = False
while (p1<len(l1) and flag) or (p2<len(l2) and not flag):
if not flag:
while p2 < len(l2) and l2[p2] < stk[-1]:
p2 += 1
if p2 < len(l2):
stk.append(l2[p2])
flag = not flag
else:
while p1 < len(l1) and l1[p1] < stk[-1]:
p1 += 1
if p1 < len(l1):
stk.append(l1[p1])
flag = not flag
ans = max(ans,len(stk))
return ans
O(n*k^2)的做法,分组O(n),自己匹配自己O(k),两两匹配O(n * k^2),明显超时的做法。