【题解】—— LeetCode一周小结21

简介: LeetCode每日一道一周小结21
🌟欢迎来到 我的博客 —— 探索技术的无限可能!


🌟博客的简介(文章目录)


【题解】—— 每日一道题目栏


上接:【题解】—— LeetCode一周小结20

20.找出最长的超赞子字符串

题目链接:1542. 找出最长的超赞子字符串

给你一个字符串 s 。请返回 s 中最长的 超赞子字符串 的长度。

「超赞子字符串」需满足满足下述两个条件:

该字符串是 s 的一个非空子字符串
进行任意次数的字符交换后,该字符串可以变成一个回文字符串

示例 1:

输入:s = "3242415"

输出:5

解释:"24241" 是最长的超赞子字符串,交换其中的字符后,可以得到回文 "24142"

示例 2:

输入:s = "12345678"

输出:1

示例 3:

输入:s = "213123"

输出:6

解释:"213123" 是最长的超赞子字符串,交换其中的字符后,可以得到回文 "231132"

示例 4:

输入:s = "00"

输出:2

提示:

1 <= s.length <= 10^5

s 仅由数字组成

题解:
方法:前缀异或和
        
要实现的方法是要接收一个字符串参数 s,并返回该字符串中最长的“好子串”的长度。

所谓“好子串”,是指满足以下条件的子串:

  • 子串中每个字符的出现次数都是偶数;
  • or子串中恰好有一个字符出现奇数次。

为了实现这个功能,使用了前缀异或和。先初始化了一个长度为 $2^D$ 的数组 pos,用于存储前缀异或和的位置。然后遍历字符串 s,计算当前前缀异或和 pre,并根据 pre 的值更新答案 ans。最后返回最长子串的长度 ans

==需要注意的是==,由于题目中的字符种类只有 10 种(即 '0' 到 '9'),因此可以使用位运算来表示前缀异或和。(eg:对于字符 c,其对应的二进制位为 $(c - '0'),因此可以通过将 1 左移 $(c - '0') 位来得到该字符的二进制位。)

class Solution {
   
    private static final int D = 10; // s 中的字符种类数

    public int longestAwesome(String s) {
   
        int n = s.length(); // 获取字符串长度
        int[] pos = new int[1 << D]; // 初始化一个长度为 2^D 的数组,用于存储前缀异或和的位置
        Arrays.fill(pos, n); // 将数组填充为 n,表示没有找到异或前缀和
        pos[0] = -1; // pre[-1] = 0,表示初始状态的前缀异或和为 0
        int ans = 0; // 初始化答案为 0
        int pre = 0; // 初始化当前前缀异或和为 0
        for (int i = 0; i < n; i++) {
    // 遍历字符串
            pre ^= 1 << (s.charAt(i) - '0'); // 更新当前前缀异或和
            for (int d = 0; d < D; d++) {
    // 遍历所有可能的字符种类
                ans = Math.max(ans, i - pos[pre ^ (1 << d)]); // 计算奇数长度的子串的最大长度
            }
            ans = Math.max(ans, i - pos[pre]); // 计算偶数长度的子串的最大长度
            if (pos[pre] == n) {
    // 如果首次遇到值为 pre 的前缀异或和,记录其下标 i
                pos[pre] = i;
            }
        }
        return ans; // 返回最长子串的长度
    }
}

==题单:前缀异或和==

1310. 子数组异或查询
1177. 构建回文串检测
1371. 每个元音包含偶数次的最长子字符串
1542. 找出最长的超赞子字符串
1915. 最美子字符串的数目
2791. 树中可以形成回文的路径数


21.找出最大的可达成数字

题目链接:2769. 找出最大的可达成数字

给你两个整数 num 和 t 。

如果整数 x 可以在执行下述操作不超过 t 次的情况下变为与 num 相等,则称其为 可达成数字 :

每次操作将 x 的值增加或减少 1 ,同时可以选择将 num 的值增加或减少 1 。
返回所有可达成数字中的最大值。可以证明至少存在一个可达成数字。

示例 1:

输入:num = 4, t = 1

输出:6

解释:最大可达成数字是 x = 6 ,执行下述操作可以使其等于 num :

  • x 减少 1 ,同时 num 增加 1 。此时,x = 5 且 num = 5 。

可以证明不存在大于 6 的可达成数字。

示例 2:

输入:num = 3, t = 2

输出:7

解释:最大的可达成数字是 x = 7 ,执行下述操作可以使其等于 num :

  • x 减少 1 ,同时 num 增加 1 。此时,x = 6 且 num = 4 。
  • x 减少 1 ,同时 num 增加 1 。此时,x = 5 且 num = 5 。

可以证明不存在大于 7 的可达成数字。

提示:

1 <= num, t <= 50

题解:
方法:数学
        每次操作可以将 x 减少 1,同时将 num 增加 1,这样 x 和 num 的差值就会减少 2,而最多可以操作 t 次,所以最大可达成数字为 num+t×2。

class Solution {
   
    public int theMaximumAchievableX(int num, int t) {
   
        return num + t * 2;
    }
}

22.找出输掉零场或一场比赛的玩家

题目链接:2225. 找出输掉零场或一场比赛的玩家

给你一个整数数组 matches 其中 matches[i] = [winneri, loseri] 表示在一场比赛中 winneri 击败了 loseri 。

返回一个长度为 2 的列表 answer :

answer[0] 是所有 没有 输掉任何比赛的玩家列表。
answer[1] 是所有恰好输掉 一场 比赛的玩家列表。
两个列表中的值都应该按 递增 顺序返回。

注意:

只考虑那些参与 至少一场 比赛的玩家。
生成的测试用例保证 不存在 两场比赛结果 相同 。

示例 1:

输入:matches =
[[1,3],[2,3],[3,6],[5,6],[5,7],[4,5],[4,8],[4,9],[10,4],[10,9]]

输出:[[1,2,10],[4,5,7,8]]

解释:

玩家 1、2 和 10 都没有输掉任何比赛。

玩家 4、5、7 和 8 每个都输掉一场比赛。

玩家 3、6 和 9 每个都输掉两场比赛。

因此,answer[0] = [1,2,10] 和 answer[1] = [4,5,7,8] 。

示例 2:

输入:matches = [[2,3],[1,3],[5,4],[6,4]]

输出:[[1,2,5,6],[]]

解释:

玩家 1、2、5 和 6 都没有输掉任何比赛。

玩家 3 和 4 每个都输掉两场比赛。

因此,answer[0] = [1,2,5,6] 和 answer[1] = [] 。

提示:

1 <= matches.length <= 105

matches[i].length == 2

1 <= winneri, loseri <= 105

winneri != loseri

所有 matches[i] 互不相同

题解:
方法:哈希表
        哈希表 cnt 用来记录每个玩家输掉的比赛场次。

        遍历哈希表,将输掉 0 场比赛的玩家放入 ans[0],将输掉 1 场比赛的玩家放入 ans[1]。

        最后将 ans[0] 和 ans[1] 按照升序排序,返回结果。

// 定义一个名为Solution的类
class Solution {
   
    // 定义一个名为findWinners的方法,接收一个二维整数数组matches作为参数
    public List<List<Integer>> findWinners(int[][] matches) {
   
        // 创建一个HashMap用于存储每个选手的比赛次数
        Map<Integer, Integer> cnt = new HashMap<>();
        // 遍历matches数组
        for (var e : matches) {
   
            // 如果e[0]不在cnt中,则将其值设为0
            cnt.putIfAbsent(e[0], 0);
            // 将e[1]的比赛次数加1,如果e[1]不存在,则将其值设为1
            cnt.merge(e[1], 1, Integer::sum);
        }
        // 创建一个包含两个ArrayList的List,分别用于存储未输过和输过一次的选手编号
        List<List<Integer>> ans = List.of(new ArrayList<>(), new ArrayList<>());
        // 遍历cnt中的键值对
        for (var e : cnt.entrySet()) {
   
            // 如果比赛次数小于2,则将选手编号添加到对应的列表中
            if (e.getValue() < 2) {
   
                ans.get(e.getValue()).add(e.getKey());
            }
        }
        // 对未输过和输过一次的选手编号列表进行排序
        Collections.sort(ans.get(0));
        Collections.sort(ans.get(1));
        // 返回包含两个列表的结果
        return ans;
    }
}

23.找出最长等值子数组

题目链接:2831. 找出最长等值子数组

给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。

如果子数组中所有元素都相等,则认为子数组是一个 等值子数组 。注意,空数组是 等值子数组 。

从 nums 中删除最多 k 个元素后,返回可能的最长等值子数组的长度。

子数组 是数组中一个连续且可能为空的元素序列。

示例 1:

输入:nums = [1,3,2,3,1,3], k = 3

输出:3

解释:最优的方案是删除下标 2 和下标 4 的元素。

删除后,nums 等于 [1, 3, 3, 3] 。

最长等值子数组从 i = 1 开始到 j = 3 结束,长度等于 3 。

可以证明无法创建更长的等值子数组。

示例 2:

输入:nums = [1,1,2,2,1,1], k = 2

输出:4

解释:最优的方案是删除下标 2 和下标 3 的元素。

删除后,nums 等于 [1, 1, 1, 1] 。

数组自身就是等值子数组,长度等于 4 。

可以证明无法创建更长的等值子数组。

提示:

1 <= nums.length <= 105

1 <= nums[i] <= nums.length

0 <= k <= nums.length

题解:
方法:滑动窗口
        遍历输入列表,对于每个元素 x,将其索引减去该元素在 posLists[x] 中已有元素的数量,然后添加到 posLists[x] 中。。

        遍历 posLists 中的每个列表,如果当前列表的长度小于等于答案,则跳过。否则,初始化左指针 left 为 0,遍历当前列表,如果右指针和左指针之间的距离大于 k,则移动左指针;更新答案为当前答案和右指针与左指针之间的距离加一的最大值。

class Solution {
   
    // 定义一个方法,用于求解最长相等子数组的长度
    public int longestEqualSubarray(List<Integer> nums, int k) {
   
        int n = nums.size(); // 获取输入列表的长度
        List<Integer>[] posLists = new ArrayList[n + 1]; // 创建一个长度为 n+1 的列表数组
        Arrays.setAll(posLists, i -> new ArrayList<>()); // 初始化数组中的每个元素为一个新的空列表
        for (int i = 0; i < n; i++) {
    // 遍历输入列表
            int x = nums.get(i); // 获取当前元素的值
            posLists[x].add(i - posLists[x].size()); // 将当前元素的索引减去该元素在 posLists[x] 中已有元素的数量,然后添加到 posLists[x] 中
        }

        int ans = 0; // 初始化答案为 0
        for (List<Integer> pos : posLists) {
    // 遍历 posLists 中的每个列表
            if (pos.size() <= ans) {
    // 如果当前列表的长度小于等于答案,则跳过
                continue;
            }
            int left = 0; // 初始化左指针为 0
            for (int right = 0; right < pos.size(); right++) {
    // 遍历当前列表
                while (pos.get(right) - pos.get(left) > k) {
    // 如果右指针和左指针之间的距离大于 k,则移动左指针
                    left++;
                }
                ans = Math.max(ans, right - left + 1); // 更新答案为当前答案和右指针与左指针之间的距离加一的最大值
            }
        }
        return ans; // 返回答案
    }
}

24.找出最具竞争力的子序列

题目链接:1673. 找出最具竞争力的子序列

给你一个整数数组 nums 和一个正整数 k ,返回长度为 k 且最具 竞争力 的 nums 子序列。

数组的子序列是从数组中删除一些元素(可能不删除元素)得到的序列。

在子序列 a 和子序列 b 第一个不相同的位置上,如果 a 中的数字小于 b 中对应的数字,那么我们称子序列 a 比子序列 b(相同长度下)更具 竞争力 。 例如,[1,3,4] 比 [1,3,5] 更具竞争力,在第一个不相同的位置,也就是最后一个位置上, 4 小于 5 。

示例 1:

输入:nums = [3,5,2,6], k = 2

输出:[2,6]

解释:在所有可能的子序列集合 {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]} 中,[2,6]
最具竞争力。

示例 2:

输入:nums = [2,4,3,3,5,4,9,6], k = 4

输出:[2,3,3,4]

提示:

1 <= nums.length <= 105

0 <= nums[i] <= 109

1 <= k <= nums.length

题解:
方法:
        我们从左到右遍历数组 nums,维护一个栈 stk,遍历过程中,如果当前元素 nums[i] 小于栈顶元素,且栈中元素个数加上 n−i 大于 k,则将栈顶元素出栈,直到不满足上述条件为止。此时如果栈中元素个数小于 k,则将当前元素入栈。遍历结束后,栈中元素即为所求。

class Solution {
   
    public int[] mostCompetitive(int[] nums, int k) {
   
        Deque<Integer> stk = new ArrayDeque<>();
        int n = nums.length;
        for (int i = 0; i < nums.length; ++i) {
   
            while (!stk.isEmpty() && stk.peek() > nums[i] && stk.size() + n - i > k) {
   
                stk.pop();
            }
            if (stk.size() < k) {
   
                stk.push(nums[i]);
            }
        }
        int[] ans = new int[stk.size()];
        for (int i = ans.length - 1; i >= 0; --i) {
   
            ans[i] = stk.pop();
        }
        return ans;
    }
}

25.找出满足差值条件的下标 I

题目链接:2903. 找出满足差值条件的下标 I

给你一个下标从 0 开始、长度为 n 的整数数组 nums ,以及整数 indexDifference 和整数 valueDifference 。

你的任务是从范围 [0, n - 1] 内找出 2 个满足下述所有条件的下标 i 和 j :

abs(i - j) >= indexDifference 且
abs(nums[i] - nums[j]) >= valueDifference
返回整数数组 answer。如果存在满足题目要求的两个下标,则 answer = [i, j] ;否则,answer = [-1, -1] 。如果存在多组可供选择的下标对,只需要返回其中任意一组即可。

注意:i 和 j 可能 相等 。

示例 1:

输入:nums = [5,1,4,1], indexDifference = 2, valueDifference = 4

输出:[0,3]

解释:在示例中,可以选择 i = 0 和 j = 3 。

abs(0 - 3) >= 2 且 abs(nums[0] - nums[3]) >= 4 。

因此,[0,3] 是一个符合题目要求的答案。

[3,0] 也是符合题目要求的答案。

示例 2:

输入:nums = [2,1], indexDifference = 0, valueDifference = 0

输出:[0,0]

解释:

在示例中,可以选择 i = 0 和 j = 0 。

abs(0 - 0) >= 0 且 abs(nums[0] - nums[0]) >= 0 。

因此,[0,0] 是一个符合题目要求的答案。

[0,1]、[1,0] 和 [1,1] 也是符合题目要求的答案。

示例 3:

输入:nums = [1,2,3], indexDifference = 2, valueDifference = 4

输出:[-1,-1]

解释:在示例中,可以证明无法找出 2 个满足所有条件的下标。

因此,返回 [-1,-1] 。

提示:

1 <= n == nums.length <= 100

0 <= nums[i] <= 50

0 <= indexDifference <= 100

0 <= valueDifference <= 50

题解:
方法:双指针
        

class Solution {
   
    public int[] findIndices(int[] nums, int indexDifference, int valueDifference) {
   
        int maxIdx = 0;
        int minIdx = 0;
        for (int j = indexDifference; j < nums.length; j++) {
   
            int i = j - indexDifference;
            if (nums[i] > nums[maxIdx]) {
   
                maxIdx = i;
            } else if (nums[i] < nums[minIdx]) {
   
                minIdx = i;
            }
            if (nums[maxIdx] - nums[j] >= valueDifference) {
   
                return new int[]{
   maxIdx, j};
            }
            if (nums[j] - nums[minIdx] >= valueDifference) {
   
                return new int[]{
   minIdx, j};
            }
        }
        return new int[]{
   -1, -1};
    }
}

26.找出第 K 大的异或坐标值

题目链接:1738. 找出第 K 大的异或坐标值

给你一个二维矩阵 matrix 和一个整数 k ,矩阵大小为 m x n 由非负整数组成。

矩阵中坐标 (a, b) 的 值 可由对所有满足 0 <= i <= a < m 且 0 <= j <= b < n 的元素 matrix[i][j](下标从 0 开始计数)执行异或运算得到。

请你找出 matrix 的所有坐标中第 k 大的值(k 的值从 1 开始计数)。

示例 1:

输入:matrix = [[5,2],[1,6]], k = 1

输出:7

解释:坐标 (0,1) 的值是 5 XOR 2 = 7 ,为最大的值。

示例 2:

输入:matrix = [[5,2],[1,6]], k = 2

输出:5

解释:坐标 (0,0) 的值是 5 = 5 ,为第 2 大的值。

示例 3:

输入:matrix = [[5,2],[1,6]], k = 3

输出:4

解释:坐标 (1,0) 的值是 5 XOR 1 = 4 ,为第 3 大的值。

示例 4:

输入:matrix = [[5,2],[1,6]], k = 4

输出:0

解释:坐标 (1,1) 的值是 5 XOR 2 XOR 1 XOR 6 = 0 ,为第 4 大的值。

提示:

m == matrix.length

n == matrix[i].length

1 <= m, n <= 1000

0 <= matrix[i][j] <= 106

1 <= k <= m * n

题解:
方法:二维前缀异或和
        

class Solution {
   
    public int kthLargestValue(int[][] matrix, int k) {
   
        int m = matrix.length;
        int n = matrix[0].length;
        int[] a = new int[m * n];
        int[][] s = new int[m + 1][n + 1];
        int idx = 0;
        for (int i = 0; i < m; i++) {
   
            for (int j = 0; j < n; j++) {
   
                s[i + 1][j + 1] = s[i + 1][j] ^ s[i][j + 1] ^ s[i][j] ^ matrix[i][j];
                a[idx++] = s[i + 1][j + 1];
            }
        }
        Arrays.sort(a);
        return a[idx - k];
    }
}

方法:列前缀异或和
        

public class Solution {
   
    public int kthLargestValue(int[][] matrix, int k) {
   
        int m = matrix.length;
        int n = matrix[0].length;
        int[] a = new int[m * n];
        int[] colSum = new int[n];
        int i = 0;
        for (int[] row : matrix) {
   
            int s = 0;
            for (int j = 0; j < row.length; j++) {
   
                colSum[j] ^= row[j];
                s ^= colSum[j];
                a[i++] = s;
            }
        }
        Arrays.sort(a);
        return a[i - k];
    }
}

下接:【题解】—— LeetCode一周小结22


目录
相关文章
|
8天前
|
人工智能 BI
【题解】—— LeetCode一周小结23
LeetCode每日一道一周小结23
14 3
【题解】—— LeetCode一周小结23
|
15天前
|
机器学习/深度学习
【题解】—— LeetCode一周小结22
LeetCode每日一道一周小结22
20 7
|
29天前
【题解】—— LeetCode一周小结20
LeetCode每日一道一周小结20
49 8
【题解】—— LeetCode一周小结20
|
1月前
|
存储 算法 索引
【题解】—— LeetCode一周小结6
【题解】—— LeetCode一周小结6
9 1
|
1月前
|
机器学习/深度学习 存储
【题解】—— LeetCode一周小结2
【题解】—— LeetCode一周小结2
7 0
|
1月前
|
索引
【题解】—— LeetCode一周小结17
【题解】—— LeetCode一周小结17
10 1
|
1月前
|
存储 算法 物联网
【题解】—— LeetCode一周小结3
【题解】—— LeetCode一周小结3
10 0
|
1月前
|
机器学习/深度学习 人工智能 算法
【题解】—— LeetCode一周小结15
【题解】—— LeetCode一周小结15
20 0
|
1月前
|
人工智能 BI
【题解】—— LeetCode一周小结9
【题解】—— LeetCode一周小结9
8 1
|
1月前
【题解】—— LeetCode一周小结8
【题解】—— LeetCode一周小结8
7 1