3005.最大频率元素计数

目标

给你一个由 正整数 组成的数组 nums 。

返回数组 nums 中所有具有 最大 频率的元素的 总频率 。

元素的 频率 是指该元素在数组中出现的次数。

示例 1:

输入:nums = [1,2,2,3,1,4]
输出:4
解释:元素 1 和 2 的频率为 2 ,是数组中的最大频率。
因此具有最大频率的元素在数组中的数量是 4 。

示例 2:

输入:nums = [1,2,3,4,5]
输出:5
解释:数组中的所有元素的频率都为 1 ,是最大频率。
因此具有最大频率的元素在数组中的数量是 5 。

说明:

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

思路

求正整数数组 nums 的最大频率之和。

针对元素计数,如果出现次数等于最大频率则累加最大频率,如果出现新的最大频率则需要将结果重置为最大频率。

代码


/**
 * @date 2025-09-22 8:48
 */
public class MaxFrequencyElements3005 {

    public int maxFrequencyElements_v1(int[] nums) {
        int res = 0;
        int[] cnt = new int[101];
        int max = 0;
        for (int num : nums) {
            cnt[num]++;
            if (max < cnt[num]) {
                max = cnt[num];
                res = cnt[num];
            } else if (max == cnt[num]) {
                res += cnt[num];
            }
        }
        return res;
    }

}

性能

1912.设计电影租借系统

目标

你有一个电影租借公司和 n 个电影商店。你想要实现一个电影租借系统,它支持查询、预订和返还电影的操作。同时系统还能生成一份当前被借出电影的报告。

所有电影用二维整数数组 entries 表示,其中 entries[i] = [shopi, moviei, pricei] 表示商店 shopi 有一份电影 moviei 的拷贝,租借价格为 pricei 。每个商店有 至多一份 编号为 moviei 的电影拷贝。

系统需要支持以下操作:

  • Search:找到拥有指定电影且 未借出 的商店中 最便宜的 5 个 。商店需要按照 价格 升序排序,如果价格相同,则 shopi 较小 的商店排在前面。如果查询结果少于 5 个商店,则将它们全部返回。如果查询结果没有任何商店,则返回空列表。
  • Rent:从指定商店借出指定电影,题目保证指定电影在指定商店 未借出
  • Drop:在指定商店返还 之前已借出 的指定电影。
  • Report:返回 最便宜的 5 部已借出电影 (可能有重复的电影 ID),将结果用二维列表 res 返回,其中 res[j] = [shopj, moviej] 表示第 j 便宜的已借出电影是从商店 shopj 借出的电影 moviej 。res 中的电影需要按 价格 升序排序;如果价格相同,则 shopj 较小 的排在前面;如果仍然相同,则 moviej 较小 的排在前面。如果当前借出的电影小于 5 部,则将它们全部返回。如果当前没有借出电影,则返回一个空的列表。

请你实现 MovieRentingSystem 类:

  • MovieRentingSystem(int n, int[][] entries) 将 MovieRentingSystem 对象用 n 个商店和 entries 表示的电影列表初始化。
  • List<Integer> search(int movie) 如上所述,返回 未借出 指定 movie 的商店列表。
  • void rent(int shop, int movie) 从指定商店 shop 借出指定电影 movie 。
  • void drop(int shop, int movie) 在指定商店 shop 返还之前借出的电影 movie 。
  • List<List<Integer>> report() 如上所述,返回最便宜的 已借出 电影列表。

注意:测试数据保证 rent 操作中指定商店拥有 未借出 的指定电影,且 drop 操作指定的商店 之前已借出 指定电影。

示例 1:

输入:
["MovieRentingSystem", "search", "rent", "rent", "report", "drop", "search"]
[[3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]], [1], [0, 1], [1, 2], [], [1, 2], [2]]
输出:
[null, [1, 0, 2], null, null, [[0, 1], [1, 2]], null, [0, 1]]

解释:
MovieRentingSystem movieRentingSystem = new MovieRentingSystem(3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]);
movieRentingSystem.search(1);  // 返回 [1, 0, 2] ,商店 1,0 和 2 有未借出的 ID 为 1 的电影。商店 1 最便宜,商店 0 和 2 价格相同,所以按商店编号排序。
movieRentingSystem.rent(0, 1); // 从商店 0 借出电影 1 。现在商店 0 未借出电影编号为 [2,3] 。
movieRentingSystem.rent(1, 2); // 从商店 1 借出电影 2 。现在商店 1 未借出的电影编号为 [1] 。
movieRentingSystem.report();   // 返回 [[0, 1], [1, 2]] 。商店 0 借出的电影 1 最便宜,然后是商店 1 借出的电影 2 。
movieRentingSystem.drop(1, 2); // 在商店 1 返还电影 2 。现在商店 1 未借出的电影编号为 [1,2] 。
movieRentingSystem.search(2);  // 返回 [0, 1] 。商店 0 和 1 有未借出的 ID 为 2 的电影。商店 0 最便宜,然后是商店 1 。

说明:

  • 1 <= n <= 3 * 10^5
  • 1 <= entries.length <= 10^5
  • 0 <= shopi < n
  • 1 <= moviei, pricei <= 10^4
  • 每个商店 至多 有一份电影 moviei 的拷贝。
  • search,rent,drop 和 report 的调用 总共 不超过 10^5 次。

思路

代码

性能

3508.设计路由器

目标

请你设计一个数据结构来高效管理网络路由器中的数据包。每个数据包包含以下属性:

  • source:生成该数据包的机器的唯一标识符。
  • destination:目标机器的唯一标识符。
  • timestamp:该数据包到达路由器的时间戳。

实现 Router 类:

Router(int memoryLimit):初始化路由器对象,并设置固定的内存限制。

  • memoryLimit 是路由器在任意时间点可以存储的 最大 数据包数量。
  • 如果添加一个新数据包会超过这个限制,则必须移除 最旧的 数据包以腾出空间。

bool addPacket(int source, int destination, int timestamp):将具有给定属性的数据包添加到路由器。

  • 如果路由器中已经存在一个具有相同 source、destination 和 timestamp 的数据包,则视为重复数据包。
  • 如果数据包成功添加(即不是重复数据包),返回 true;否则返回 false。

int[] forwardPacket():以 FIFO(先进先出)顺序转发下一个数据包。

  • 从存储中移除该数据包。
  • 以数组 [source, destination, timestamp] 的形式返回该数据包。
  • 如果没有数据包可以转发,则返回空数组。

int getCount(int destination, int startTime, int endTime):

  • 返回当前存储在路由器中(即尚未转发)的,且目标地址为指定 destination 且时间戳在范围 [startTime, endTime](包括两端)内的数据包数量。

注意:对于 addPacket 的查询会按照 timestamp 的递增顺序进行。

示例 1:

输入:
["Router", "addPacket", "addPacket", "addPacket", "addPacket", "addPacket", "forwardPacket", "addPacket", "getCount"]
[[3], [1, 4, 90], [2, 5, 90], [1, 4, 90], [3, 5, 95], [4, 5, 105], [], [5, 2, 110], [5, 100, 110]]
输出:
[null, true, true, false, true, true, [2, 5, 90], true, 1]
解释:
Router router = new Router(3); // 初始化路由器,内存限制为 3。
router.addPacket(1, 4, 90); // 数据包被添加,返回 True。
router.addPacket(2, 5, 90); // 数据包被添加,返回 True。
router.addPacket(1, 4, 90); // 这是一个重复数据包,返回 False。
router.addPacket(3, 5, 95); // 数据包被添加,返回 True。
router.addPacket(4, 5, 105); // 数据包被添加,[1, 4, 90] 被移除,因为数据包数量超过限制,返回 True。
router.forwardPacket(); // 转发数据包 [2, 5, 90] 并将其从路由器中移除。
router.addPacket(5, 2, 110); // 数据包被添加,返回 True。
router.getCount(5, 100, 110); // 唯一目标地址为 5 且时间在 [100, 110] 范围内的数据包是 [4, 5, 105],返回 1。

示例 2:

输入:
["Router", "addPacket", "forwardPacket", "forwardPacket"]
[[2], [7, 4, 90], [], []]
输出:
[null, true, [7, 4, 90], []]
解释:
Router router = new Router(2); // 初始化路由器,内存限制为 2。
router.addPacket(7, 4, 90); // 返回 True。
router.forwardPacket(); // 返回 [7, 4, 90]。
router.forwardPacket(); // 没有数据包可以转发,返回 []。

说明:

  • 2 <= memoryLimit <= 10^5
  • 1 <= source, destination <= 2 * 10^5
  • 1 <= timestamp <= 10^9
  • 1 <= startTime <= endTime <= 10^9
  • addPacket、forwardPacket 和 getCount 方法的总调用次数最多为 10^5。
  • 对于 addPacket 的查询,timestamp 按递增顺序给出。

思路

//todo

代码

性能

3484.设计电子表格

目标

电子表格是一个网格,它有 26 列(从 'A' 到 'Z')和指定数量的 rows。每个单元格可以存储一个 0 到 10^5 之间的整数值。

请你实现一个 Spreadsheet 类:

  • Spreadsheet(int rows) 初始化一个具有 26 列(从 'A' 到 'Z')和指定行数的电子表格。所有单元格最初的值都为 0 。
  • void setCell(String cell, int value) 设置指定单元格的值。单元格引用以 "AX" 的格式提供(例如,"A1","B10"),其中字母表示列(从 'A' 到 'Z'),数字表示从 1 开始的行号。
  • void resetCell(String cell) 重置指定单元格的值为 0 。
  • int getValue(String formula) 计算一个公式的值,格式为 "=X+Y",其中 X 和 Y 要么 是单元格引用,要么非负整数,返回计算的和。

注意: 如果 getValue 引用一个未通过 setCell 明确设置的单元格,则该单元格的值默认为 0 。

示例 1:

输入:
["Spreadsheet", "getValue", "setCell", "getValue", "setCell", "getValue", "resetCell", "getValue"]
[[3], ["=5+7"], ["A1", 10], ["=A1+6"], ["B2", 15], ["=A1+B2"], ["A1"], ["=A1+B2"]]
输出:
[null, 12, null, 16, null, 25, null, 15]
解释
Spreadsheet spreadsheet = new Spreadsheet(3); // 初始化一个具有 3 行和 26 列的电子表格
spreadsheet.getValue("=5+7"); // 返回 12 (5+7)
spreadsheet.setCell("A1", 10); // 设置 A1 为 10
spreadsheet.getValue("=A1+6"); // 返回 16 (10+6)
spreadsheet.setCell("B2", 15); // 设置 B2 为 15
spreadsheet.getValue("=A1+B2"); // 返回 25 (10+15)
spreadsheet.resetCell("A1"); // 重置 A1 为 0
spreadsheet.getValue("=A1+B2"); // 返回 15 (0+15)

说明:

  • 1 <= rows <= 10^3
  • 0 <= value <= 10^5
  • 公式保证采用 "=X+Y" 格式,其中 X 和 Y 要么是有效的单元格引用,要么是小于等于 10^5 的 非负 整数。
  • 每个单元格引用由一个大写字母 'A' 到 'Z' 和一个介于 1 和 rows 之间的行号组成。
  • 总共 最多会对 setCell、resetCell 和 getValue 调用 10^4 次。

思路

依题意模拟即可。

代码


/**
 * @date 2025-09-19 8:38
 */
public class Spreadsheet {

    private final int[][] data;

    public Spreadsheet(int rows) {
        data = new int[26][rows + 1];
    }

    public void setCell(String cell, int value) {
        int col = cell.charAt(0) - 'A';
        int row = Integer.parseInt(cell.substring(1));
        data[col][row] = value;
    }

    public void resetCell(String cell) {
        int col = cell.charAt(0) - 'A';
        int row = Integer.parseInt(cell.substring(1));
        data[col][row] = 0;
    }

    public int getValue(String formula) {
        String[] params = formula.substring(1).split("\\+");
        int res = 0;
        for (String param : params) {
            if (param.charAt(0) < 'A' || param.charAt(0) > 'Z') {
                res += Integer.parseInt(param);
            } else {
                int col = param.charAt(0) - 'A';
                int row = Integer.parseInt(param.substring(1));
                res += data[col][row];
            }
        }
        return res;
    }
}

性能

3408.设计任务管理器

目标

一个任务管理器系统可以让用户管理他们的任务,每个任务有一个优先级。这个系统需要高效地处理添加、修改、执行和删除任务的操作。

请你设计一个 TaskManager 类:

TaskManager(vector<vector>& tasks) 初始化任务管理器,初始化的数组格式为 [userId, taskId, priority] ,表示给 userId 添加一个优先级为 priority 的任务 taskId 。

void add(int userId, int taskId, int priority) 表示给用户 userId 添加一个优先级为 priority 的任务 taskId ,输入 保证 taskId 不在系统中。

void edit(int taskId, int newPriority) 更新已经存在的任务 taskId 的优先级为 newPriority 。输入 保证 taskId 存在于系统中。

void rmv(int taskId) 从系统中删除任务 taskId 。输入 保证 taskId 存在于系统中。

int execTop() 执行所有用户的任务中优先级 最高 的任务,如果有多个任务优先级相同且都为 最高 ,执行 taskId 最大的一个任务。执行完任务后,taskId 从系统中 删除 。同时请你返回这个任务所属的用户 userId 。如果不存在任何任务,返回 -1 。

注意 ,一个用户可能被安排多个任务。

示例 1:

输入:
["TaskManager", "add", "edit", "execTop", "rmv", "add", "execTop"]
[[[[1, 101, 10], [2, 102, 20], [3, 103, 15]]], [4, 104, 5], [102, 8], [], [101], [5, 105, 15], []]
输出:
[null, null, null, 3, null, null, 5]
解释:
TaskManager taskManager = new TaskManager([[1, 101, 10], [2, 102, 20], [3, 103, 15]]); // 分别给用户 1 ,2 和 3 初始化一个任务。
taskManager.add(4, 104, 5); // 给用户 4 添加优先级为 5 的任务 104 。
taskManager.edit(102, 8); // 更新任务 102 的优先级为 8 。
taskManager.execTop(); // 返回 3 。执行用户 3 的任务 103 。
taskManager.rmv(101); // 将系统中的任务 101 删除。
taskManager.add(5, 105, 15); // 给用户 5 添加优先级为 15 的任务 105 。
taskManager.execTop(); // 返回 5 。执行用户 5 的任务 105 。

说明:

  • 1 <= tasks.length <= 10^5
  • 0 <= userId <= 10^5
  • 0 <= taskId <= 10^5
  • 0 <= priority <= 10^9
  • 0 <= newPriority <= 10^9
  • add ,edit ,rmv 和 execTop 的总操作次数 加起来 不超过 2 * 10^5 次。
  • 输入保证 taskId 是合法的。

思路

2349.设计数字容器系统 类似,同样是懒删除。

代码


/**
 * @date 2025-09-18 8:42
 */
public class TaskManager3408 {

    static class TaskManager {

        private final Map<Integer, int[]> taskIdMap = new HashMap<>();
        private final PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> {
            int compare = b[2] - a[2];
            if (compare != 0) {
                return compare;
            }
            return b[1] - a[1];
        });

        public TaskManager(List<List<Integer>> tasks) {
            for (List<Integer> task : tasks) {
                int[] t = {task.get(0), task.get(1), task.get(2)};
                taskIdMap.put(t[1], t);
                q.offer(t);
            }
        }

        public void add(int userId, int taskId, int priority) {
            int[] t = {userId, taskId, priority};
            taskIdMap.put(taskId, t);
            q.offer(t);
        }

        public void edit(int taskId, int newPriority) {
            int[] t = taskIdMap.get(taskId);
            t = new int[]{t[0], t[1], newPriority};
            taskIdMap.put(taskId, t);
            q.offer(t);
        }

        public void rmv(int taskId) {
            taskIdMap.remove(taskId);
        }

        public int execTop() {
            while (!q.isEmpty() && (taskIdMap.get(q.peek()[1]) == null || taskIdMap.get(q.peek()[1])[2] != q.peek()[2]|| taskIdMap.get(q.peek()[1])[0] != q.peek()[0])) {
                q.poll();
            }
            if (q.isEmpty()) {
                return -1;
            }
            int[] task = q.poll();
            taskIdMap.remove(task[1]);
            return task[0];
        }
    }

}

性能

2349.设计数字容器系统

目标

设计一个数字容器系统,可以实现以下功能:

  • 在系统中给定下标处 插入 或者 替换 一个数字。
  • 返回 系统中给定数字的最小下标。

请你实现一个 NumberContainers 类:

  • NumberContainers() 初始化数字容器系统。
  • void change(int index, int number) 在下标 index 处填入 number 。如果该下标 index 处已经有数字了,那么用 number 替换该数字。
  • int find(int number) 返回给定数字 number 在系统中的最小下标。如果系统中没有 number ,那么返回 -1 。

示例:

输入:
["NumberContainers", "find", "change", "change", "change", "change", "find", "change", "find"]
[[], [10], [2, 10], [1, 10], [3, 10], [5, 10], [10], [1, 20], [10]]
输出:
[null, -1, null, null, null, null, 1, null, 2]
解释:
NumberContainers nc = new NumberContainers();
nc.find(10); // 没有数字 10 ,所以返回 -1 。
nc.change(2, 10); // 容器中下标为 2 处填入数字 10 。
nc.change(1, 10); // 容器中下标为 1 处填入数字 10 。
nc.change(3, 10); // 容器中下标为 3 处填入数字 10 。
nc.change(5, 10); // 容器中下标为 5 处填入数字 10 。
nc.find(10); // 数字 10 所在的下标为 1 ,2 ,3 和 5 。因为最小下标为 1 ,所以返回 1 。
nc.change(1, 20); // 容器中下标为 1 处填入数字 20 。注意,下标 1 处之前为 10 ,现在被替换为 20 。
nc.find(10); // 数字 10 所在下标为 2 ,3 和 5 。最小下标为 2 ,所以返回 2 。

说明:

  • 1 <= index, number <= 10^9
  • 调用 change 和 find 的 总次数 不超过 10^5 次。

思路

设计一个数字容器,能够将数字更新到指定下标,并且查询数字的最小下标。

使用最小堆维护相同数字的最小下标,同时使用哈希表记录下标对应的数字。查询最小下标时,如果下标上的数字不是查询的数字则从堆中删除。

代码


/**
 * @date 2025-09-17 8:48
 */
public class NumberContainers {

    private final Map<Integer, Integer> indexToValue;
    private final Map<Integer, PriorityQueue<Integer>> valueToIndex;

    public NumberContainers() {
        indexToValue = new HashMap<>();
        valueToIndex = new HashMap<>();
    }

    public void change(int index, int number) {
        indexToValue.put(index, number);
        valueToIndex.putIfAbsent(number, new PriorityQueue<>());
        valueToIndex.get(number).offer(index);
    }

    public int find(int number) {
        PriorityQueue<Integer> q = valueToIndex.get(number);
        if (q == null) {
            return -1;
        }
        while (!q.isEmpty() && indexToValue.get(q.peek()) != number) {
            q.poll();
        }
        return q.isEmpty() ? -1 : q.peek();
    }
}

性能

2197.替换数组中的非互质数

目标

给你一个整数数组 nums 。请你对数组执行下述操作:

  1. 从 nums 中找出 任意 两个 相邻 的 非互质 数。
  2. 如果不存在这样的数,终止 这一过程。
  3. 否则,删除这两个数,并 替换 为它们的 最小公倍数(Least Common Multiple,LCM)。
  4. 只要还能找出两个相邻的非互质数就继续 重复 这一过程。

返回修改后得到的 最终 数组。可以证明的是,以 任意 顺序替换相邻的非互质数都可以得到相同的结果。

生成的测试用例可以保证最终数组中的值 小于或者等于 10^8 。

两个数字 x 和 y 满足 非互质数 的条件是:GCD(x, y) > 1 ,其中 GCD(x, y) 是 x 和 y 的 最大公约数 。

示例 1 :

输入:nums = [6,4,3,2,7,6,2]
输出:[12,7,6]
解释:
- (6, 4) 是一组非互质数,且 LCM(6, 4) = 12 。得到 nums = [12,3,2,7,6,2] 。
- (12, 3) 是一组非互质数,且 LCM(12, 3) = 12 。得到 nums = [12,2,7,6,2] 。
- (12, 2) 是一组非互质数,且 LCM(12, 2) = 12 。得到 nums = [12,7,6,2] 。
- (6, 2) 是一组非互质数,且 LCM(6, 2) = 6 。得到 nums = [12,7,6] 。
现在,nums 中不存在相邻的非互质数。
因此,修改后得到的最终数组是 [12,7,6] 。
注意,存在其他方法可以获得相同的最终数组。

示例 2 :

输入:nums = [2,2,1,1,3,3,3]
输出:[2,1,1,3]
解释:
- (3, 3) 是一组非互质数,且 LCM(3, 3) = 3 。得到 nums = [2,2,1,1,3,3] 。
- (3, 3) 是一组非互质数,且 LCM(3, 3) = 3 。得到 nums = [2,2,1,1,3] 。
- (2, 2) 是一组非互质数,且 LCM(2, 2) = 2 。得到 nums = [2,1,1,3] 。
现在,nums 中不存在相邻的非互质数。 
因此,修改后得到的最终数组是 [2,1,1,3] 。 
注意,存在其他方法可以获得相同的最终数组。

说明:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5
  • 生成的测试用例可以保证最终数组中的值 小于或者等于 10^8 。

思路

将数组 nums 中相邻的非互质的数用它们的最小公倍数替换,一直重复这一过程,返回最终的数组。

遍历数组,针对每一个 num 判断它与其左侧已处理的最后一个值(即 list 的最后一个元素)是否互质,如果不互质,将 num 替换为它们的最小公倍数,同时删除 list 最后一个元素,重复该过程,直到互质为止,最后将 num 加入 list

代码


/**
 * @date 2025-09-16 8:45
 */
public class ReplaceNonCoprimes2197 {

    public List<Integer> replaceNonCoprimes(int[] nums) {
        List<Integer> list = new ArrayList<>();
        for (int a : nums) {
            while (!list.isEmpty()) {
                int b = list.get(list.size() - 1);
                int g = gcd(a, b);
                if (g <= 1) {
                    break;
                }
                a = a / g * b;
                list.remove(list.size() - 1);
            }
            list.add(a);
        }
        return list;
    }

}

性能

1935.可以输入的最大单词数

目标

键盘出现了一些故障,有些字母键无法正常工作。而键盘上所有其他键都能够正常工作。

给你一个由若干单词组成的字符串 text ,单词间由单个空格组成(不含前导和尾随空格);另有一个字符串 brokenLetters ,由所有已损坏的不同字母键组成,返回你可以使用此键盘完全输入的 text 中单词的数目。

示例 1:

输入:text = "hello world", brokenLetters = "ad"
输出:1
解释:无法输入 "world" ,因为字母键 'd' 已损坏。

示例 2:

输入:text = "leet code", brokenLetters = "lt"
输出:1
解释:无法输入 "leet" ,因为字母键 'l' 和 't' 已损坏。

示例 3:

输入:text = "leet code", brokenLetters = "e"
输出:0
解释:无法输入任何单词,因为字母键 'e' 已损坏。

说明:

  • 1 <= text.length <= 10^4
  • 0 <= brokenLetters.length <= 26
  • text 由若干用单个空格分隔的单词组成,且不含任何前导和尾随空格
  • 每个单词仅由小写英文字母组成
  • brokenLetters 由 互不相同 的小写英文字母组成

思路

有一个字符串,其中的单词由空格分隔,现在键盘有一些按键不能正常工作,返回能够正常输入的单词个数。

使用哈希表记录不正常的按键,分割单词,判断单词是否包含不正常字符。

代码


/**
 * @date 2025-09-15 8:46
 */
public class CanBeTypedWords1935 {

    public int canBeTypedWords(String text, String brokenLetters) {
        Set<Character> brokenSet = new HashSet<>();
        for (char c : brokenLetters.toCharArray()) {
            brokenSet.add(c);
        }
        String[] words = text.split(" ");
        int res = words.length;
        for (String word : words) {
            for (char c : word.toCharArray()) {
                if (brokenSet.contains(c)){
                    res--;
                    break;
                }
            }
        }
        return res;
    }
}

性能

966.元音拼写检查器

目标

在给定单词列表 wordlist 的情况下,我们希望实现一个拼写检查器,将查询单词转换为正确的单词。

对于给定的查询单词 query,拼写检查器将会处理两类拼写错误:

  • 大小写:如果查询匹配单词列表中的某个单词(不区分大小写),则返回的正确单词与单词列表中的大小写相同。
    • 例如:wordlist = ["yellow"], query = "YellOw": correct = "yellow"
    • 例如:wordlist = ["Yellow"], query = "yellow": correct = "Yellow"
    • 例如:wordlist = ["yellow"], query = "yellow": correct = "yellow"
  • 元音错误:如果在将查询单词中的元音 ('a', 'e', 'i', 'o', 'u') 分别替换为任何元音后,能与单词列表中的单词匹配(不区分大小写),则返回的正确单词与单词列表中的匹配项大小写相同。
    • 例如:wordlist = ["YellOw"], query = "yollow": correct = "YellOw"
    • 例如:wordlist = ["YellOw"], query = "yeellow": correct = "" (无匹配项)
    • 例如:wordlist = ["YellOw"], query = "yllw": correct = "" (无匹配项)

此外,拼写检查器还按照以下优先级规则操作:

  • 当查询完全匹配单词列表中的某个单词(区分大小写)时,应返回相同的单词。
  • 当查询匹配到大小写问题的单词时,您应该返回单词列表中的第一个这样的匹配项。
  • 当查询匹配到元音错误的单词时,您应该返回单词列表中的第一个这样的匹配项。
  • 如果该查询在单词列表中没有匹配项,则应返回空字符串。

给出一些查询 queries,返回一个单词列表 answer,其中 answer[i] 是由查询 query = queries[i] 得到的正确单词。

示例 1:

输入:wordlist = ["KiTe","kite","hare","Hare"], queries = ["kite","Kite","KiTe","Hare","HARE","Hear","hear","keti","keet","keto"]
输出:["kite","KiTe","KiTe","Hare","hare","","","KiTe","","KiTe"]

示例 2:

输入:wordlist = ["yellow"], queries = ["YellOw"]
输出:["yellow"]

说明:

  • 1 <= wordlist.length, queries.length <= 5000
  • 1 <= wordlist[i].length, queries[i].length <= 7
  • wordlist[i] 和 queries[i] 只包含英文字母

思路

有一个单词列表 wordlist,将输入单词 query 按照下面规则转换为列表中的单词:

  • 如果 query 在 wordlist 中直接返回
  • 如果 query 忽略大小写后在 wordlist 中返回第一个匹配的单词
  • 如果 query 忽略大小并且其中的元音替换为任意元音后在 wordlist 中返回第一个匹配的单词
  • 如果以上都没有匹配返回空串

依题意模拟即可。创建三个哈希表,key 分别为 原单词、忽略大小写后的单词以及 将元音字母替换为通配符后的单词。

代码


/**
 * @date 2025-09-14 21:25
 */
public class Spellchecker966 {

    public String[] spellchecker(String[] wordlist, String[] queries) {
        Set<String> originSet = new HashSet<>();
        Map<String, List<String>> caseMap = new HashMap<>();
        Map<String, List<String>> vowelMap = new HashMap<>();
        for (String word : wordlist) {
            originSet.add(word);
            String key = word.toLowerCase();
            caseMap.putIfAbsent(key, new ArrayList<>());
            caseMap.get(key).add(word);
            String vowelKey = key.replaceAll("[aeiouAEIOU]", "*");
            vowelMap.putIfAbsent(vowelKey, new ArrayList<>());
            vowelMap.get(vowelKey).add(word);
        }
        for (int i = 0; i < queries.length; i++) {
            String query = queries[i];
            if (originSet.contains(query)) {
                continue;
            }
            String key = query.toLowerCase();
            List<String> caseList = caseMap.get(key);
            if (caseList != null && caseList.size() > 0) {
                queries[i] = caseList.get(0);
                continue;
            }
            List<String> vowelList = vowelMap.get(key.replaceAll("[aeiouAEIOU]", "*"));
            if (vowelList != null && vowelList.size() > 0) {
                queries[i] = vowelList.get(0);
                continue;
            }
            queries[i] = "";
        }
        return queries;
    }

}

性能

3541.找到频率最高的元音和辅音

目标

给你一个由小写英文字母('a' 到 'z')组成的字符串 s。你的任务是找出出现频率 最高 的元音('a'、'e'、'i'、'o'、'u' 中的一个)和出现频率最高的辅音(除元音以外的所有字母),并返回这两个频率之和。

注意:如果有多个元音或辅音具有相同的最高频率,可以任选其中一个。如果字符串中没有元音或没有辅音,则其频率视为 0。

一个字母 x 的 频率 是它在字符串中出现的次数。

示例 1:

输入: s = "successes"
输出: 6
解释:
元音有:'u' 出现 1 次,'e' 出现 2 次。最大元音频率 = 2。
辅音有:'s' 出现 4 次,'c' 出现 2 次。最大辅音频率 = 4。
输出为 2 + 4 = 6。

示例 2:

输入: s = "aeiaeia"
输出: 3
解释:
元音有:'a' 出现 3 次,'e' 出现 2 次,'i' 出现 2 次。最大元音频率 = 3。
s 中没有辅音。因此,最大辅音频率 = 0。
输出为 3 + 0 = 3。

说明:

  • 1 <= s.length <= 100
  • s 只包含小写英文字母

思路

计算字符串中辅音字母与元音字母的最高频率之和。

代码


/**
 * @date 2025-09-13 22:46
 */
public class MaxFreqSum3541 {

    public int maxFreqSum(String s) {
        int[] cnt = new int[26];
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (char c : s.toCharArray()) {
            int i = c - 'a';
            if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
                cnt[i]--;
                min = Math.min(min, cnt[i]);
            } else {
                cnt[i]++;
                max = Math.max(max, cnt[i]);
            }
        }
        return Math.max(0, max) - Math.min(0, min);
    }
}

性能