链接:LeetCode
[Leetcode]2367. 算术三元组的数目
给你一个下标从 0 开始、严格递增 的整数数组 nums 和一个正整数 diff 。如果满足下述全部条件,则三元组 (i, j, k) 就是一个 算术三元组 :
- i < j < k ,
- nums[j] - nums[i] == diff 且
- nums[k] - nums[j] == diff
返回不同 算术三元组 的数目。
由于是严格递增的,哈希即可。
class Solution {
public int arithmeticTriplets(int[] nums, int diff) {
int res = 0;
HashSet<Integer> set = new HashSet<>();
for(var num:nums) set.add(num);
for(var num:nums) {
if(set.contains(num+diff) && set.contains(num+2*diff)) {
res ++;
}
}
return res;
}
}
[Leetcode]2368. 受限条件下可到达节点的数目
现有一棵由 n 个节点组成的无向树,节点编号从 0 到 n - 1 ,共有 n - 1 条边。
给你一个二维整数数组 edges ,长度为 n - 1 ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条边。另给你一个整数数组 restricted 表示 受限 节点。
在不访问受限节点的前提下,返回你可以从节点 0 到达的 最多 节点数目。
注意,节点 0 不 会标记为受限节点。
BFS或DFS。构建全局双向图,从0开始,遍历全图。
class Solution {
public int reachableNodes(int n, int[][] edges, int[] restricted) {
HashMap<Integer, HashSet<Integer>> hash = new HashMap<>();
HashSet<Integer> restrictedSet = new HashSet<>();
for(var re:restricted) restrictedSet.add(re);
for(var edge:edges) {
int i = edge[0], j = edge[1];
if(!restrictedSet.contains(i) && !restrictedSet.contains(j)) {
hash.computeIfAbsent(i, key -> new HashSet<Integer>()).add(j);
hash.computeIfAbsent(j, key -> new HashSet<Integer>()).add(i);
}
}
int res = 0;
Deque<Integer> queue = new ArrayDeque<>();
HashSet<Integer> visited = new HashSet<>();
queue.addLast(0);
while(!queue.isEmpty()) {
int val = queue.pollFirst();
visited.add(val);
res++;
for(int nxt:hash.getOrDefault(val, new HashSet<Integer>())) {
if(visited.contains(nxt)) continue;
queue.addLast(nxt);
}
}
return res;
}
}
[Leetcode]2369. 检查数组是否存在有效划分
给你一个下标从 0 开始的整数数组 nums ,你必须将数组划分为一个或多个 连续 子数组。
如果获得的这些子数组中每个都能满足下述条件 之一 ,则可以称其为数组的一种 有效 划分:
- 子数组 恰 由 2 个相等元素组成,例如,子数组 [2,2] 。
- 子数组 恰 由 3 个相等元素组成,例如,子数组 [4,4,4] 。
- 子数组 恰 由 3 个连续递增元素组成,并且相邻元素之间的差值为 1 。例如,子数组 [3,4,5] ,但是子数组 [1,3,5] 不符合要求。
如果数组 至少 存在一种有效划分,返回 true ,否则,返回 false 。
DP.
定义 \(f[i+1]\) 表示从 \(\textit{nums}[0]\) 到 \(\textit{nums}[i]\) 的这些元素能否有效划分。那么 \(f[0] = \texttt{true}\),答案为 \(f[n]\)。
根据题意,有
class Solution {
public boolean validPartition(int[] nums) {
var n = nums.length;
var f = new boolean[n + 1];
f[0] = true;
for (var i = 1; i < n; ++i)
if (f[i - 1] && nums[i] == nums[i - 1] ||
i > 1 && f[i - 2] && (nums[i] == nums[i - 1] && nums[i] == nums[i - 2] ||
nums[i] == nums[i - 1] + 1 && nums[i] == nums[i - 2] + 2))
f[i + 1] = true;
return f[n];
}
}
[Leetcode]2370. 最长理想子序列
给你一个由小写字母组成的字符串 s ,和一个整数 k 。如果满足下述条件,则可以将字符串 t 视作是 理想字符串 :
- t 是字符串 s 的一个子序列。
- t 中每两个 相邻 字母在字母表中位次的绝对差值小于或等于 k 。
返回 最长 理想字符串的长度。
字符串的子序列同样是一个字符串,并且子序列还满足:可以经由其他字符串删除某些字符(也可以不删除)但不改变剩余字符的顺序得到。
注意:字母表顺序不会循环。例如,'a' 和 'z' 在字母表中位次的绝对差值是 25 ,而不是 1 。
子序列 DP + 枚举字符 + 空间优化.看到子序列和相邻就可以往 DP 上想。
class Solution {
public int longestIdealString(String s, int k) {
int n = s.length();
int[] index = new int[26];
Arrays.fill(index, -1);
int[] dp = new int[n];
Arrays.fill(dp, 1);
for(int i=0;i<n;++i) {
char ch = s.charAt(i);
int idx = ch - 'a';
if(i == 0) {
index[idx] = i;
dp[i] = 1;
} else {
for(int j = Math.max(idx-k, 0); j < Math.min(idx+k+1, 26); ++j) {
if(index[j] == -1) continue;
else dp[i] = Math.max(dp[i], dp[index[j]] + 1);
}
index[idx] = i;
}
}
return Arrays.stream(dp).max().getAsInt();
}
}
参考:LeetCode