第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

news2025/6/12 16:58:13

Q1、[中等] 矩阵中的幻方

1、题目描述

3 x 3 的幻方是一个填充有 19 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。

给定一个由整数组成的row x colgrid,其中有多少个 3 × 3 的 “幻方” 子矩阵?

注意:虽然幻方只能包含 1 到 9 的数字,但 grid 可以包含最多15的数字。

示例 1:

img

输入: grid = [[4,3,8,4],[9,5,1,9],[2,7,6,2]
输出: 1
解释: 
下面的子矩阵是一个 3 x 3 的幻方:

而这一个不是:

总的来说,在本示例所给定的矩阵中只有一个 3 x 3 的幻方子矩阵。

示例 2:

输入: grid = [[8]]
输出: 0

提示:

  • row == grid.length
  • col == grid[i].length
  • 1 <= row, col <= 10
  • 0 <= grid[i][j] <= 15
2、解题思路
  1. 问题分析

    • 需要遍历 grid 中所有可能的 3 × 3 子矩阵。
    • 对于每个子矩阵,检查它是否满足幻方的条件。
  2. 幻方的条件

    • 包含 19 的不同数字。
    • 每行、每列以及两条对角线的和都等于 15
  3. 优化

    • 幻方的中心必须是 5,因此可以提前过滤掉中心不是 5 的子矩阵。
  4. 算法设计

    • 遍历 grid 中所有可能的 3 × 3 子矩阵。
    • 对于每个子矩阵,检查是否满足幻方的条件。
    • 统计满足条件的子矩阵数量。
3、代码实现
C++
class Solution {
public:
    // 检查一个 3x3 矩阵是否是幻方
    bool ismagic(array<int, 9> m) {
        int count[16] = {0}; // 用于统计数字出现的次数
        for (auto n : m) {
            // 检查数字是否在 1 到 9 之间且不重复
            if (n < 1 || n > 9 || ++count[n] > 1) {
                return false;
            }
        }
        // 检查每行、每列以及两条对角线的和是否等于 15
        return m[0] + m[1] + m[2] == 15 && m[3] + m[4] + m[5] == 15 &&
               m[6] + m[7] + m[8] == 15 && m[0] + m[3] + m[6] == 15 &&
               m[1] + m[4] + m[7] == 15 && m[2] + m[5] + m[8] == 15 &&
               m[0] + m[4] + m[8] == 15 && m[2] + m[4] + m[6] == 15;
    }

    int numMagicSquaresInside(vector<vector<int>>& grid) {
        if (grid.size() < 3 || grid[0].size() < 3) {
            return 0; // 如果 grid 的大小小于 3x3,直接返回 0
        }
        int m = grid.size(), n = grid[0].size();
        int res = 0;                           // 记录幻方的数量
        for (int i = 0; i < m - 2; i++) {      // 遍历所有可能的起始行
            for (int j = 0; j < n - 2; j++) {  // 遍历所有可能的起始列
                if (grid[i + 1][j + 1] != 5) { // 如果中心不是 5,跳过
                    continue;
                }
                array<int, 9> v; // 用于存储 3x3 子矩阵的元素
                int w = 0;
                for (int r = i; r < i + 3; r++) { // 提取 3x3 子矩阵
                    for (int c = j; c < j + 3; c++) {
                        v[w++] = grid[r][c];
                    }
                }
                res += ismagic(v); // 检查是否是幻方
            }
        }
        return res; // 返回幻方的数量
    }
};
Java
class Solution {
    // 检查一个 3x3 矩阵是否是幻方
    private boolean ismagic(int[] m) {
        int[] count = new int[16]; // 用于统计数字出现的次数
        for (int n : m) {
            if (n < 1 || n > 9 || ++count[n] > 1) { // 检查数字是否在 1 到 9 之间且不重复
                return false;
            }
        }
        // 检查每行、每列以及两条对角线的和是否等于 15
        return  m[0] + m[1] + m[2] == 15 && m[3] + m[4] + m[5] == 15 &&
                m[6] + m[7] + m[8] == 15 && m[0] + m[3] + m[6] == 15 &&
                m[1] + m[4] + m[7] == 15 && m[2] + m[5] + m[8] == 15 &&
                m[0] + m[4] + m[8] == 15 && m[2] + m[4] + m[6] == 15;
    }

    public int numMagicSquaresInside(int[][] grid) {
        if (grid.length < 3 || grid[0].length < 3) {
            return 0; // 如果 grid 的大小小于 3x3,直接返回 0
        }
        int m = grid.length, n = grid[0].length;
        int res = 0; // 记录幻方的数量
        for (int i = 0; i < m - 2; i++) { // 遍历所有可能的起始行
            for (int j = 0; j < n - 2; j++) { // 遍历所有可能的起始列
                if (grid[i + 1][j + 1] != 5) { // 如果中心不是 5,跳过
                    continue;
                }
                int[] v = new int[9]; // 用于存储 3x3 子矩阵的元素
                int w = 0;
                for (int r = i; r < i + 3; r++) { // 提取 3x3 子矩阵
                    for (int c = j; c < j + 3; c++) {
                        v[w++] = grid[r][c];
                    }
                }
                res += ismagic(v) ? 1 : 0; // 检查是否是幻方
            }
        }
        return res; // 返回幻方的数量
    }
}
Python
class Solution:
    def ismagic(self, m):
        count = [0] * 16  # 用于统计数字出现的次数
        for n in m:
            if n < 1 or n > 9 or count[n] > 0:  # 检查数字是否在 1 到 9 之间且不重复
                return False
            count[n] += 1
        # 检查每行、每列以及两条对角线的和是否等于 15
        return (
            m[0] + m[1] + m[2] == 15
            and m[3] + m[4] + m[5] == 15
            and m[6] + m[7] + m[8] == 15
            and m[0] + m[3] + m[6] == 15
            and m[1] + m[4] + m[7] == 15
            and m[2] + m[5] + m[8] == 15
            and m[0] + m[4] + m[8] == 15
            and m[2] + m[4] + m[6] == 15
        )

    def numMagicSquaresInside(self, grid):
        if len(grid) < 3 or len(grid[0]) < 3:
            return 0  # 如果 grid 的大小小于 3x3,直接返回 0
        m, n = len(grid), len(grid[0])
        res = 0  # 记录幻方的数量
        for i in range(m - 2):  # 遍历所有可能的起始行
            for j in range(n - 2):  # 遍历所有可能的起始列
                if grid[i + 1][j + 1] != 5:  # 如果中心不是 5,跳过
                    continue
                v = [
                    grid[r][c] for r in range(i, i + 3) for c in range(j, j + 3)
                ]  # 提取 3x3 子矩阵
                res += 1 if self.ismagic(v) else 0  # 检查是否是幻方
        return res  # 返回幻方的数量

在这里插入图片描述

4、复杂度分析
  1. 时间复杂度

    • 遍历所有可能的 3 × 3 子矩阵:O((m−2)×(n−2))。
    • 检查每个子矩阵是否是幻方:O(1)。
    • 总时间复杂度:O((m−2)×(n−2))。
  2. 空间复杂度

    • 使用常数级别的额外空间,空间复杂度为 O(1)。

Q2、[中等] 钥匙和房间

1、题目描述

n 个房间,房间按从 0n - 1 编号。最初,除 0 号房间外的其余所有房间都被锁住。你的目标是进入所有的房间。然而,你不能在没有获得钥匙的时候进入锁住的房间。

当你进入一个房间,你可能会在里面找到一套 不同的钥匙,每把钥匙上都有对应的房间号,即表示钥匙可以打开的房间。你可以拿上所有钥匙去解锁其他房间。

给你一个数组 rooms 其中 rooms[i] 是你进入 i 号房间可以获得的钥匙集合。如果能进入 所有 房间返回 true,否则返回 false

示例 1:

输入:rooms = [[1],[2],[3],[]]
输出:true
解释:
我们从 0 号房间开始,拿到钥匙 1。
之后我们去 1 号房间,拿到钥匙 2。
然后我们去 2 号房间,拿到钥匙 3。
最后我们去了 3 号房间。
由于我们能够进入每个房间,我们返回 true。

示例 2:

输入:rooms = [[1,3],[3,0,1],[2],[0]]
输出:false
解释:我们不能进入 2 号房间。

提示:

  • n == rooms.length
  • 2 <= n <= 1000
  • 0 <= rooms[i].length <= 1000
  • 1 <= sum(rooms[i].length) <= 3000
  • 0 <= rooms[i][j] < n
  • 所有 rooms[i] 的值 互不相同
2、解题思路
  1. 问题分析

    • 房间和钥匙的关系可以看作是一个图,其中房间是节点,钥匙是边。
    • 0 号房间开始,通过钥匙逐步解锁其他房间。
    • 需要判断是否可以从 0 号房间访问到所有其他房间。
  2. 算法设计

    • 使用广度优先搜索(BFS)或深度优先搜索(DFS)遍历图。
    • 0 号房间开始,将所有可以访问的房间加入集合。
    • 最终检查集合的大小是否等于 n
  3. 优化

    • 使用 BFS 或 DFS 确保所有可达房间都被访问。
3、代码实现
C++
class Solution {
public:
    bool canVisitAllRooms(vector<vector<int>>& rooms) {
        int n = rooms.size();       // 房间的数量
        unordered_set<int> visited; // 记录已访问的房间
        queue<int> q;               // BFS 队列
        q.push(0);                  // 从 0 号房间开始
        visited.insert(0);          // 标记 0 号房间为已访问

        while (!q.empty()) {       // BFS 过程
            int index = q.front(); // 取出当前房间
            q.pop();
            for (const auto& key : rooms[index]) { // 遍历当前房间的钥匙
                if (!visited.count(key)) {         // 如果钥匙对应的房间未被访问
                    q.push(key);                   // 加入队列
                    visited.insert(key);           // 标记为已访问
                }
            }
        }

        return visited.size() == n; // 判断是否访问了所有房间
    }
};
class Solution {
private:
    vector<int> visited;
    int num;

    void dfs(vector<vector<int>>& rooms, int x) {
        visited[x] = true;
        num++;
        for (const auto& it : rooms[x]) {
            if (!visited[it]) {
                dfs(rooms, it);
            }
        }
    }

public:
    bool canVisitAllRooms(vector<vector<int>>& rooms) {
        int n = rooms.size();
        num = 0;
        visited.resize(n);
        dfs(rooms, 0);
        return num == n;
    }
};
Java
class Solution {
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        int n = rooms.size(); // 房间的数量
        Set<Integer> visited = new HashSet<>(); // 记录已访问的房间
        Queue<Integer> q = new LinkedList<>(); // BFS 队列
        q.offer(0); // 从 0 号房间开始
        visited.add(0); // 标记 0 号房间为已访问

        while (!q.isEmpty()) { // BFS 过程
            int index = q.poll(); // 取出当前房间
            for (int key : rooms.get(index)) { // 遍历当前房间的钥匙
                if (!visited.contains(key)) { // 如果钥匙对应的房间未被访问
                    q.offer(key); // 加入队列
                    visited.add(key); // 标记为已访问
                }
            }
        }

        return visited.size() == n; // 判断是否访问了所有房间
    }
}
Python
class Solution:
    def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
        n = len(rooms)  # 房间的数量
        visited = set()  # 记录已访问的房间
        q = deque()  # BFS 队列
        q.append(0)  # 从 0 号房间开始
        visited.add(0)  # 标记 0 号房间为已访问

        while q:  # BFS 过程
            index = q.popleft()  # 取出当前房间
            for key in rooms[index]:  # 遍历当前房间的钥匙
                if key not in visited:  # 如果钥匙对应的房间未被访问
                    q.append(key)  # 加入队列
                    visited.add(key)  # 标记为已访问

        return len(visited) == n  # 判断是否访问了所有房间

在这里插入图片描述

4、复杂度分析
  1. 时间复杂度
    • 每个房间和钥匙最多被访问一次,时间复杂度为 O*(*n+k),其中 n 是房间的数量,k 是钥匙的总数。
  2. 空间复杂度
    • 使用 visited 集合和队列 q,空间复杂度为 O(n)。

Q3、[中等] 将数组拆分成斐波那契序列

1、题目描述

给定一个数字字符串 num,比如 "123456579",我们可以将它分成「斐波那契式」的序列 [123, 456, 579]

形式上,斐波那契式 序列是一个非负整数列表 f,且满足:

  • 0 <= f[i] < 231 ,(也就是说,每个整数都符合 32 位 有符号整数类型)
  • f.length >= 3
  • 对于所有的0 <= i < f.length - 2,都有 f[i] + f[i + 1] = f[i + 2]

另外,请注意,将字符串拆分成小块时,每个块的数字一定不要以零开头,除非这个块是数字 0 本身。

返回从 num 拆分出来的任意一组斐波那契式的序列块,如果不能拆分则返回 []

示例 1:

输入:num = "1101111"
输出:[11,0,11,11]
解释:输出 [110,1,111] 也可以。

示例 2:

输入: num = "112358130"
输出: []
解释: 无法拆分。

示例 3:

输入:"0123"
输出:[]
解释:每个块的数字不能以零开头,因此 "01","2","3" 不是有效答案。

提示:

  • 1 <= num.length <= 200
  • num 中只含有数字
2、解题思路
  1. 问题分析

    • 需要将字符串 num 拆分成一个斐波那契式序列。
    • 序列中的每个整数必须满足 0 <= f[i] < 2^31
    • 序列的长度至少为 3,且满足斐波那契性质。
    • 拆分时,不能有前导零(除非是 0 本身)。
  2. 算法设计

    • 使用回溯法枚举所有可能的拆分方式。
    • 对于每个可能的拆分,检查是否满足斐波那契性质。
    • 如果找到满足条件的序列,则返回结果。
  3. 优化

    • 在回溯过程中,如果当前数字超过 2^31 - 1,则直接剪枝。

    • 如果当前数字不满足斐波那契性质,则剪枝。

3、代码实现
C++
class Solution {
public:
    vector<int> splitIntoFibonacci(string num) {
        vector<int> list;                            // 用于存储斐波那契序列
        backtrack(list, num, num.length(), 0, 0, 0); // 调用回调函数
        return list;                                 // 返回结果
    }

    bool backtrack(vector<int>& list, string num, int length, int index, long long sum, int prev) {
        if (index == length) {
            return list.size() >= 3; // 如果遍历完字符串序列且序列长度 >= 3, 返回 true
        }
        long long curr = 0; // 当前数字
        for (int i = index; i < length; ++i) {
            if (i > index && num[index] == '0') {
                break; // 如果有前导零, 直接剪枝
            }
            curr = curr * 10 + (num[i] - '0'); // 计算当前数字
            if (curr > INT_MAX) {
                break; // 如果当前数字超过 2^31 - 1, 剪枝
            }
            if (list.size() >= 2) {
                if (curr < sum) {
                    continue; // 如果当前数字小于 sum, 继续增加数字
                } else if (curr > sum) {
                    break; // 如果当前数字大于 sum,剪枝
                }
            }
            list.push_back(curr); // 将当前数字加入序列
            if (backtrack(list, num, length, i + 1, prev + curr, curr)) {
                return true;
            }
            list.pop_back(); // 回溯, 移除当前数字
        }
        return false; // 如果没有找到满足条件的序列, 返回 false
    }
};
Java
class Solution {
    public List<Integer> splitIntoFibonacci(String num) {
        List<Integer> list = new ArrayList<>(); // 用于存储斐波那契序列
        backtrack(list, num, num.length(), 0, 0, 0); // 调用回溯函数
        return list; // 返回结果
    }

    private boolean backtrack(List<Integer> list, String num, int length, int index, long sum, long prev) {
        if (index == length) {
            return list.size() >= 3; // 如果遍历完字符串且序列长度 >= 3,返回 true
        }
        long curr = 0; // 当前数字
        for (int i = index; i < length; i++) {
            if (i > index && num.charAt(index) == '0') {
                break; // 如果有前导零,直接剪枝
            }
            curr = curr * 10 + (num.charAt(i) - '0'); // 计算当前数字
            if (curr > Integer.MAX_VALUE) {
                break; // 如果当前数字超过 2^31 - 1,剪枝
            }
            if (list.size() >= 2) {
                if (curr < sum) {
                    continue; // 如果当前数字小于 sum,继续增加数字
                } else if (curr > sum) {
                    break; // 如果当前数字大于 sum,剪枝
                }
            }
            list.add((int) curr); // 将当前数字加入序列
            if (backtrack(list, num, length, i + 1, prev + curr, curr)) {
                return true; // 如果找到满足条件的序列,返回 true
            }
            list.remove(list.size() - 1); // 回溯,移除当前数字
        }
        return false; // 如果没有找到满足条件的序列,返回 false
    }
}
Python
class Solution:
    def splitIntoFibonacci(self, num: str) -> List[int]:
        def backtrack(index, sum_val, prev, path):
            if index == len(num):
                return len(path) >= 3  # 如果遍历完字符串且序列长度 >= 3,返回 True
            curr = 0  # 当前数字
            for i in range(index, len(num)):
                if i > index and num[index] == "0":
                    break  # 如果有前导零,直接剪枝
                curr = curr * 10 + int(num[i])  # 计算当前数字
                if curr > 2**31 - 1:
                    break  # 如果当前数字超过 2^31 - 1,剪枝
                if len(path) >= 2:
                    if curr < sum_val:
                        continue  # 如果当前数字小于 sum,继续增加数字
                    elif curr > sum_val:
                        break  # 如果当前数字大于 sum,剪枝
                path.append(curr)  # 将当前数字加入序列
                if backtrack(i + 1, prev + curr, curr, path):
                    return True  # 如果找到满足条件的序列,返回 True
                path.pop()  # 回溯,移除当前数字
            return False  # 如果没有找到满足条件的序列,返回 False

        result = []
        backtrack(0, 0, 0, result)
        return result

在这里插入图片描述

4、复杂度分析
  1. 时间复杂度

    • 最坏情况下,回溯的时间复杂度为 O(2n),其中 n 是字符串的长度。
    • 由于剪枝的存在,实际运行时间会大大减少。
  2. 空间复杂度

    • 使用递归栈和结果列表,空间复杂度为 O(n)。

Q4、[困难] 猜猜这个单词

1、题目描述

给你一个由 不同 字符串组成的单词列表 words ,其中 words[i] 长度均为 6words 中的一个单词将被选作秘密单词 secret

另给你一个辅助对象 Master ,你可以调用 Master.guess(word) 来猜单词,其中参数 word 长度为 6 且必须是 words 中的字符串。

Master.guess(word) 将会返回如下结果:

  • 如果 word 不是 words 中的字符串,返回 -1 ,或者
  • 一个整数,表示你所猜测的单词 word秘密单词 secret 的准确匹配(值和位置同时匹配)的数目。

每组测试用例都会包含一个参数 allowedGuesses ,其中 allowedGuesses 是你可以调用 Master.guess(word) 的最大次数。

对于每组测试用例,在不超过允许猜测的次数的前提下,你应该调用 Master.guess 来猜出秘密单词。最终,你将会得到以下结果:

  • 如果你调用 Master.guess 的次数大于 allowedGuesses 所限定的次数或者你没有用 Master.guess 猜到秘密单词,则得到 "Either you took too many guesses, or you did not find the secret word."
  • 如果你调用 Master.guess 猜到秘密单词,且调用 Master.guess 的次数小于或等于 allowedGuesses ,则得到 "You guessed the secret word correctly."

生成的测试用例保证你可以利用某种合理的策略(而不是暴力)猜到秘密单词。

示例 1:

输入:secret = "acckzz", words = ["acckzz","ccbazz","eiowzz","abcczz"], allowedGuesses = 10
输出:You guessed the secret word correctly.
解释:
master.guess("aaaaaa") 返回 -1 ,因为 "aaaaaa" 不在 words 中。
master.guess("acckzz") 返回 6 ,因为 "acckzz" 是秘密单词 secret ,共有 6 个字母匹配。
master.guess("ccbazz") 返回 3 ,因为 "ccbazz" 共有 3 个字母匹配。
master.guess("eiowzz") 返回 2 ,因为 "eiowzz" 共有 2 个字母匹配。
master.guess("abcczz") 返回 4 ,因为 "abcczz" 共有 4 个字母匹配。
一共调用 5 次 master.guess ,其中一个为秘密单词,所以通过测试用例。

示例 2:

输入:secret = "hamada", words = ["hamada","khaled"], allowedGuesses = 10
输出:You guessed the secret word correctly.
解释:共有 2 个单词,且其中一个为秘密单词,可以通过测试用例。

提示:

  • 1 <= words.length <= 100
  • words[i].length == 6
  • words[i] 仅由小写英文字母组成
  • words 中所有字符串 互不相同
  • secret 存在于 words
  • 10 <= allowedGuesses <= 30
2、解题思路
  1. 问题分析
    • 需要从 words 中猜出秘密单词 secret
    • 每次调用 Master.guess(word) 会返回 wordsecret 的匹配数目。
    • 需要在允许的猜测次数内找到 secret
  2. 算法设计
    • 使用启发式方法,每次选择一个单词进行猜测,并根据返回的匹配数目缩小可能的候选单词范围。
    • 通过计算单词之间的匹配数目,选择能够最大程度减少候选单词数量的单词进行猜测。
  3. 优化
    • 使用预计算的匹配矩阵 H,其中 H[i][j] 表示 words[i]words[j] 的匹配数目。
    • 在每次猜测后,根据匹配数目过滤候选单词。
3、代码实现
C++
class Solution {
private:
    vector<vector<int>> H; // 匹配矩阵, H[i][j] 表示 words[i] 和 words[j] 的匹配数目

    // 选择下一个猜测的单词
    int solve(vector<int>& possible, vector<int>& path) {
        if (possible.size() <= 2) {
            return possible[0]; // 如果候选单词数量 <= 2,直接返回第一个
        }
        vector<int> ansgrp = possible;
        int ansguess = -1;

        // 遍历所有可能的猜测单词
        for (int guess = 0; guess < H.size(); ++guess) {
            if (find(path.begin(), path.end(), guess) ==
                path.end()) {                  // 如果 guess 未被猜测过
                vector<vector<int>> groups(7); // 根据匹配数目分组
                for (int j : possible) {
                    if (j != guess) {
                        groups[H[guess][j]].push_back(j);
                    }
                }

                // 找到最大的组
                vector<int> maxgroup = groups[0];
                for (int i = 0; i < 7; ++i) {
                    if (groups[i].size() > maxgroup.size()) {
                        maxgroup = groups[i];
                    }
                }

                // 选择能够最小化最大组的猜测单词
                if (maxgroup.size() < ansgrp.size()) {
                    ansgrp = maxgroup;
                    ansguess = guess;
                }
            }
        }

        return ansguess;
    }

public:
    void findSecretWord(vector<string>& words, Master& master) {
        int N = words.size();
        H = vector<vector<int>>(N, vector<int>(N, 0)); // 初始化匹配矩阵
        for (int i = 0; i < N; ++i) {
            for (int j = i; j < N; ++j) {
                int match = 0;
                for (int k = 0; k < 6; ++k) {
                    if (words[i][k] == words[j][k]) {
                        match++;
                    }
                }
                H[i][j] = H[j][i] = match; // 计算匹配数目
            }
        }

        vector<int> possible; // 候选单词列表
        vector<int> path;     // 已猜测的单词列表
        for (int i = 0; i < N; ++i) {
            possible.push_back(i);
        }

        while (!possible.empty()) {
            int guess = solve(possible, path);        // 选择下一个猜测单词
            int matches = master.guess(words[guess]); // 调用 Master.guess
            if (matches == words[0].length()) {
                return; // 如果猜中,直接返回
            }
            vector<int> possible2; // 新的候选单词列表
            for (int j : possible) {
                if (H[guess][j] == matches) {
                    possible2.push_back(j); // 根据匹配数目过滤候选单词
                }
            }
            possible = possible2;
            path.push_back(guess); // 将猜测单词加入已猜测列表
        }
    }
};
Java
class Solution {
    private int[][] H; // 匹配矩阵,H[i][j] 表示 words[i] 和 words[j] 的匹配数目

    // 选择下一个猜测的单词
    private int solve(List<Integer> possible, List<Integer> path) {
        if (possible.size() <= 2) {
            return possible.get(0); // 如果候选单词数量 <= 2,直接返回第一个
        }
        List<Integer> ansgrp = possible;
        int ansguess = -1;

        // 遍历所有可能的猜测单词
        for (int guess = 0; guess < H.length; guess++) {
            if (!path.contains(guess)) { // 如果 guess 未被猜测过
                List<List<Integer>> groups = new ArrayList<>(7);
                for (int i = 0; i < 7; i++) {
                    groups.add(new ArrayList<>());
                }
                for (int j : possible) {
                    if (j != guess) {
                        groups.get(H[guess][j]).add(j); // 根据匹配数目分组
                    }
                }

                // 找到最大的组
                List<Integer> maxgroup = groups.get(0);
                for (int i = 0; i < 7; i++) {
                    if (groups.get(i).size() > maxgroup.size()) {
                        maxgroup = groups.get(i);
                    }
                }

                // 选择能够最小化最大组的猜测单词
                if (maxgroup.size() < ansgrp.size()) {
                    ansgrp = maxgroup;
                    ansguess = guess;
                }
            }
        }

        return ansguess;
    }

    public void findSecretWord(String[] words, Master master) {
        int N = words.length;
        H = new int[N][N]; // 初始化匹配矩阵
        for (int i = 0; i < N; i++) {
            for (int j = i; j < N; j++) {
                int match = 0;
                for (int k = 0; k < 6; k++) {
                    if (words[i].charAt(k) == words[j].charAt(k)) {
                        match++;
                    }
                }
                H[i][j] = H[j][i] = match; // 计算匹配数目
            }
        }

        List<Integer> possible = new ArrayList<>(); // 候选单词列表
        List<Integer> path = new ArrayList<>(); // 已猜测的单词列表
        for (int i = 0; i < N; i++) {
            possible.add(i);
        }

        while (!possible.isEmpty()) {
            int guess = solve(possible, path); // 选择下一个猜测单词
            int matches = master.guess(words[guess]); // 调用 Master.guess
            if (matches == words[0].length()) {
                return; // 如果猜中,直接返回
            }
            List<Integer> possible2 = new ArrayList<>(); // 新的候选单词列表
            for (int j : possible) {
                if (H[guess][j] == matches) {
                    possible2.add(j); // 根据匹配数目过滤候选单词
                }
            }
            possible = possible2;
            path.add(guess); // 将猜测单词加入已猜测列表
        }
    }
}
Python
class Solution:
    def __init__(self):
        self.H = []  # 匹配矩阵,H[i][j] 表示 words[i] 和 words[j] 的匹配数目

    # 选择下一个猜测的单词
    def solve(self, possible: List[int], path: List[int]) -> int:
        if len(possible) <= 2:
            return possible[0]  # 如果候选单词数量 <= 2,直接返回第一个
        ansgrp = possible
        ansguess = -1

        # 遍历所有可能的猜测单词
        for guess in range(len(self.H)):
            if guess not in path:  # 如果 guess 未被猜测过
                groups = [[] for _ in range(7)]  # 根据匹配数目分组
                for j in possible:
                    if j != guess:
                        groups[self.H[guess][j]].append(j)

                # 找到最大的组
                maxgroup = groups[0]
                for i in range(7):
                    if len(groups[i]) > len(maxgroup):
                        maxgroup = groups[i]

                # 选择能够最小化最大组的猜测单词
                if len(maxgroup) < len(ansgrp):
                    ansgrp = maxgroup
                    ansguess = guess

        return ansguess

    def findSecretWord(self, words: List[str], master: "Master") -> None:
        N = len(words)
        self.H = [[0] * N for _ in range(N)]  # 初始化匹配矩阵
        for i in range(N):
            for j in range(i, N):
                match = 0
                for k in range(6):
                    if words[i][k] == words[j][k]:
                        match += 1
                self.H[i][j] = self.H[j][i] = match  # 计算匹配数目

        possible = list(range(N))  # 候选单词列表
        path = []  # 已猜测的单词列表

        while possible:
            guess = self.solve(possible, path)  # 选择下一个猜测单词
            matches = master.guess(words[guess])  # 调用 Master.guess
            if matches == len(words[0]):
                return  # 如果猜中,直接返回
            possible = [
                j for j in possible if self.H[guess][j] == matches
            ]  # 根据匹配数目过滤候选单词
            path.append(guess)  # 将猜测单词加入已猜测列表

在这里插入图片描述

4、复杂度分析
  1. 时间复杂度
    • 时间复杂度:O(N2logN),其中 N 是单词的总数
  2. 空间复杂度
    • 空间复杂度:O(N2)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2407489.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…