[Leetcode Weekly Contest]361
链接:LeetCode
[Leetcode]2843. 统计对称整数的数目
给你两个正整数 low 和 high 。
对于一个由 2 * n 位数字组成的整数 x ,如果其前 n 位数字之和与后 n 位数字之和相等,则认为这个数字是一个对称整数。
返回在 [low, high] 范围内的 对称整数的数目 。
遍历即可。
class Solution {
public int countSymmetricIntegers(int low, int high) {
int res = 0;
for(int i=low;i<=high;++i) {
if(isSymmetricIntegers(i)) {
res ++;
}
}
return res;
}
private boolean isSymmetricIntegers(int val) {
int n = (""+val).length();
if((n&1)!=0) return false;
n = n/2;
int val1 = 0, val2 = 0, i = 0;
while(val != 0) {
int num = val % 10;
val = val / 10;
if(i < n) val1 += num;
else val2 += num;
++i;
}
return val1 == val2;
}
}
[Leetcode]2844. 生成特殊数字的最少操作
给你一个下标从 0 开始的字符串 num ,表示一个非负整数。
在一次操作中,您可以选择 num 的任意一位数字并将其删除。请注意,如果你删除 num 中的所有数字,则 num 变为 0。
返回最少需要多少次操作可以使 num 变成特殊数字。
如果整数 x 能被 25 整除,则该整数 x 被认为是特殊数字。
枚举删除后以 00/25/50/75 结尾
class Solution {
public int minimumOperations(String num) {
int res = num.length();
boolean getZero = false, getFive = false;
int n = num.length();
for(int i=n-1;i>=0;--i) {
char ch = num.charAt(i);
if(ch == '0') {
if(!getZero) getZero = true;
else return n-(i+2);
}
if(ch == '5') {
if(!getFive) getFive = true;
if(getZero) return n-(i+2);
}
if(ch == '2' || ch == '7') {
if(getFive) return n-(i+2);
}
}
return getZero ? res-1 : res;
}
}
[Leetcode]2845. 统计趣味子数组的数目
给你一个下标从 0 开始的整数数组 nums ,以及整数 modulo 和整数 k 。
请你找出并统计数组中 趣味子数组 的数目。
如果 子数组 nums[l..r] 满足下述条件,则称其为 趣味子数组 :
在范围 [l, r] 内,设 cnt 为满足 nums[i] % modulo == k 的索引 i 的数量。并且 cnt % modulo == k 。
以整数形式表示并返回趣味子数组的数目。
注意:子数组是数组中的一个连续非空的元素序列。
前缀和+数学式子变形。对于本题,由于需要统计 \(\textit{cnt}\),我们可以把满足要求的nums[i] 视作 1,不满足为0, 如此转换后,算出 \(\textit{nums}\) 的前缀和数组 s,那么题目中的cnt mod modulo=k等价于满足 (s[r+1]−s[l])mod modulo=k, 对于这个等式,等同于 \(s[r+1]\%modulo -s[l]\%modulo = k\), 考虑到负数的情况,即\((s[r+1]\%modulo -s[l]\%modulo + modulo) \% modulo = k\)
class Solution {
public long countInterestingSubarrays(List<Integer> nums, int modulo, int k) {
int n = nums.size();
int[] presum = new int[n+1];
int sum = 0;
for(int i=0;i<n;++i) {
if(nums.get(i) % modulo == k) {
sum ++;
}
presum[i+1] = sum;
}
Map<Integer, Integer> map = new HashMap<>();
long res = 0L;
for(int num:presum) {
res += map.getOrDefault((num % modulo - k + modulo) % modulo, 0);
map.put(num % modulo, map.getOrDefault(num % modulo, 0) + 1);
}
return res;
}
}
[Leetcode]2846. 边权重均等查询
现有一棵由 n 个节点组成的无向树,节点按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ui, vi, wi] 表示树中存在一条位于节点 ui 和节点 vi 之间、权重为 wi 的边。
另给你一个长度为 m 的二维整数数组 queries ,其中 queries[i] = [ai, bi] 。对于每条查询,请你找出使从 ai 到 bi 路径上每条边的权重相等所需的 最小操作次数 。在一次操作中,你可以选择树上的任意一条边,并将其权重更改为任意值。
注意:
- 查询之间 相互独立 的,这意味着每条新的查询时,树都会回到 初始状态 。
- 从 ai 到 bi的路径是一个由 不同 节点组成的序列,从节点 ai 开始,到节点 bi 结束,且序列中相邻的两个节点在树中共享一条边。
返回一个长度为 m 的数组 answer ,其中 answer[i] 是第 i 条查询的答案。
LCA 模板,对于每个询问,在计算 a 和 b 的最近公共祖先的同时,也同样地维护从 a 到 b 路径上的每种边权的个数 cnt。
class Solution {
public int[] minOperationsQueries(int n, int[][] edges, int[][] queries) {
List<int[]>[] g = new ArrayList[n];
Arrays.setAll(g, e -> new ArrayList<>());
for (var e : edges) {
int x = e[0], y = e[1], w = e[2] - 1;
g[x].add(new int[]{y, w});
g[y].add(new int[]{x, w});
}
int m = 32 - Integer.numberOfLeadingZeros(n); // n 的二进制长度
var pa = new int[n][m];
for (int i = 0; i < n; i++) {
Arrays.fill(pa[i], -1);
}
var cnt = new int[n][m][26];
var depth = new int[n];
dfs(0, -1, g, pa, cnt, depth);
for (int i = 0; i < m - 1; i++) {
for (int x = 0; x < n; x++) {
int p = pa[x][i];
if (p != -1) {
int pp = pa[p][i];
pa[x][i + 1] = pp;
for (int j = 0; j < 26; j++) {
cnt[x][i + 1][j] = cnt[x][i][j] + cnt[p][i][j];
}
}
}
}
var ans = new int[queries.length];
for (int qi = 0; qi < queries.length; qi++) {
int x = queries[qi][0], y = queries[qi][1];
int pathLen = depth[x] + depth[y];
var cw = new int[26];
if (depth[x] > depth[y]) {
int temp = x;
x = y;
y = temp;
}
// 让 y 和 x 在同一深度
for (int k = depth[y] - depth[x]; k > 0; k &= k - 1) {
int i = Integer.numberOfTrailingZeros(k);
int p = pa[y][i];
for (int j = 0; j < 26; ++j) {
cw[j] += cnt[y][i][j];
}
y = p;
}
if (y != x) {
for (int i = m - 1; i >= 0; i--) {
int px = pa[x][i];
int py = pa[y][i];
if (px != py) {
for (int j = 0; j < 26; j++) {
cw[j] += cnt[x][i][j] + cnt[y][i][j];
}
x = px;
y = py; // x 和 y 同时上跳 2^i 步
}
}
for (int j = 0; j < 26; j++) {
cw[j] += cnt[x][0][j] + cnt[y][0][j];
}
x = pa[x][0];
}
int lca = x;
pathLen -= depth[lca] * 2;
int maxCw = 0;
for (int i = 0; i < 26; i++) {
maxCw = Math.max(maxCw, cw[i]);
}
ans[qi] = pathLen - maxCw;
}
return ans;
}
private void dfs(int x, int fa, List<int[]>[] g, int[][] pa, int[][][] cnt, int[] depth) {
pa[x][0] = fa;
for (var e : g[x]) {
int y = e[0], w = e[1];
if (y != fa) {
cnt[y][0][w] = 1;
depth[y] = depth[x] + 1;
dfs(y, x, g, pa, cnt, depth);
}
}
}
}
参考:LeetCode