华为OD机试真题——最长的顺子(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
2025 A卷 100分 题型
本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析;
并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式!
本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》
华为OD机试真题《最长的顺子》:
目录
- 题目名称:最长的顺子
- 题目描述
- Java
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- python
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- JavaScript
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- C++
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入(无顺子):
- 示例3输入(完整顺子):
- 综合分析
- C语言
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入(无顺子):
- 示例3输入(最长顺子):
- 综合分析
- GO
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入(无顺子):
- 示例3输入(最长顺子):
- 综合分析
- 更多内容:
题目名称:最长的顺子
知识点:字符串、动态规划/滑动窗口、逻辑处理
时间限制:1秒
空间限制:256MB
语言限制:不限
题目描述
输入当前手牌和已出牌,计算对手可能组成的最长顺子(连续5-12张牌,不含2和大小王)。若存在多个最长顺子,输出牌面最大的那个,否则返回"NO-CHAIN"。
规则说明
- 顺子定义:连续递增的牌序列,范围从3到A(3<4<5<6<7<8<9<10<J<Q<K<A),不含2、B(小王)、C(大王)。
- 输入格式:
- 第一行为当前手牌(如
3-3-4-5-A
) - 第二行为已出牌(如
A-4-5-6-7
)
- 第一行为当前手牌(如
- 输出格式:最长顺子,若长度相同则输出牌面最大的(如
9-10-J-Q-K-A
优于5-6-7-8-9
)。 - 数据约束:每张牌初始有4张(除大小王),总牌数为54张。
输入示例
3-3-4-4-5-A-5-6-2-8-3-9-10-Q-7-K-J-10-B
A-4-5-8-8-10-C-6-7-8
输出示例
9-10-J-Q-K-A
Java
问题分析
我们需要找到对手可能组成的最长顺子。顺子由3到A的连续牌组成,不含2和大小王。输出最长顺子,若存在多个相同长度的,选择牌面最大的那个。若无法组成,返回"NO-CHAIN"。
解题思路
- 统计剩余牌数:根据手牌和已出牌,计算每个有效牌的剩余数量。
- 滑动窗口遍历:从每个可能的起始牌开始,尽可能向右扩展窗口,直到遇到剩余不足的牌。
- 选择最优结果:记录最长窗口,若长度相同则选择末尾最大的窗口。
代码实现
import java.util.*;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 读取输入的两行并分割成牌列表String handInput = scanner.nextLine().trim();String playedInput = scanner.nextLine().trim();List<String> allCards = new ArrayList<>();if (!handInput.isEmpty()) {allCards.addAll(Arrays.asList(handInput.split("-")));}if (!playedInput.isEmpty()) {allCards.addAll(Arrays.asList(playedInput.split("-")));}// 有效牌的顺序和集合String[] order = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};Set<String> validCards = new HashSet<>(Arrays.asList(order));// 统计每个牌的出现次数Map<String, Integer> countMap = new HashMap<>();for (String card : allCards) {if (validCards.contains(card)) {countMap.put(card, countMap.getOrDefault(card, 0) + 1);}}// 计算每个牌的剩余次数int[] remain = new int[order.length];for (int i = 0; i < order.length; i++) {String card = order[i];int used = countMap.getOrDefault(card, 0);remain[i] = 4 - used; // 每张牌最多4张}// 滑动窗口遍历寻找最长顺子int maxLen = 0;int bestStart = 0;int bestEnd = 0;for (int i = 0; i < order.length; i++) {if (remain[i] <= 0) continue; // 起始牌必须可用int currentEnd = i;// 向右扩展窗口,直到遇到不可用的牌for (int j = i; j < order.length; j++) {if (remain[j] <= 0) break;currentEnd = j;}int currentLen = currentEnd - i + 1;// 更新最长且牌面最大的顺子if (currentLen >= 5) {if (currentLen > maxLen || (currentLen == maxLen && currentEnd > bestEnd)) {maxLen = currentLen;bestStart = i;bestEnd = currentEnd;}}}// 输出结果if (maxLen >= 5) {List<String> result = new ArrayList<>();for (int i = bestStart; i <= bestEnd; i++) {result.add(order[i]);}System.out.println(String.join("-", result));} else {System.out.println("NO-CHAIN");}}
}
代码详细解析
- 输入处理:读取手牌和已出牌,分割为列表。
- 有效牌集合:定义合法的顺子牌和顺序。
- 统计次数:遍历所有牌,统计每个有效牌的出现次数。
- 剩余计算:每种牌最多4张,剩余次数为
4 - 已用次数
。 - 滑动窗口:
- 外层遍历每个起始点
i
,若该牌剩余可用则继续。 - 内层扩展窗口
j
,直到遇到剩余为0的牌,记录窗口结束位置。 - 更新最长且最大的顺子。
- 外层遍历每个起始点
- 结果输出:根据窗口生成顺子字符串或输出“NO-CHAIN”。
示例测试
示例1输入:
3-3-4-4-5-A-5-6-2-8-3-9-10-Q-7-K-J-10-B
A-4-5-8-8-10-C-6-7-8
输出:9-10-J-Q-K-A
解析:剩余牌中9到A的连续序列最长且牌面最大。
示例2输入(无法形成顺子):
3-3-3-3
4-4-4-4
输出:NO-CHAIN
示例3输入(最长顺子):
3-4-5-6-7-8-9-10-J-Q-K-A
(空已出牌)
输出:3-4-5-6-7-8-9-10-J-Q-K-A
综合分析
- 时间复杂度:O(n²),n为有效牌数量(12),双重循环遍历。
- 空间复杂度:O(n),存储剩余次数和牌顺序。
- 优势:滑动窗口确保找到最优解,处理高效。
- 适用场景:适用于题目约束的小规模输入,快速找到最长顺子。
python
问题分析
我们需要找到对手可能组成的最长顺子。顺子由3到A的连续牌组成,不含2和大小王。输出最长顺子,若存在多个相同长度的,选择牌面最大的那个。若无法组成,返回"NO-CHAIN"。
解题思路
- 输入处理:将手牌和已出牌合并,统计每张牌的出现次数。
- 剩余牌计算:每张牌最多4张,减去已出现的次数得到剩余数量。
- 滑动窗口遍历:从每个可能的起点出发,寻找最长的连续可用牌序列。
- 最优顺子选择:记录最长且最大的顺子。
代码实现
def main():# 读取输入的两行数据hand = input().strip().split('-') if input().strip() != '' else []played = input().strip().split('-') if input().strip() != '' else []all_cards = hand + played# 定义有效牌的顺序(从3到A)order = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]valid_cards = set(order) # 用于快速判断是否为有效牌# 统计每张牌的使用次数count = {}for card in all_cards:if card in valid_cards:count[card] = count.get(card, 0) + 1# 计算每张牌的剩余数量(总牌数为4)remain = {card: 4 - count.get(card, 0) for card in order}max_length = 0 # 最长顺子的长度best_start = 0 # 最长顺子的起始索引best_end = -1 # 最长顺子的结束索引# 遍历所有可能的起始点for i in range(len(order)):# 如果当前牌不可用,跳过if remain[order[i]] <= 0:continuecurrent_end = i # 当前窗口的结束位置# 从i出发,尽可能向右扩展窗口for j in range(i, len(order)):if remain[order[j]] <= 0:break # 遇到不可用的牌,停止扩展current_end = j# 计算当前窗口的长度current_length = current_end - i + 1# 更新最长顺子(长度优先,其次比较结束索引)if current_length >= 5:if (current_length > max_length) or \(current_length == max_length and current_end > best_end):max_length = current_lengthbest_start = ibest_end = current_end# 输出结果if max_length >= 5:chain = order[best_start : best_end+1]print("-".join(chain))else:print("NO-CHAIN")if __name__ == "__main__":main()
代码详细解析
-
输入处理:
- 读取手牌和已出牌,分割为列表(
split('-')
)。 - 合并所有牌到
all_cards
列表。
- 读取手牌和已出牌,分割为列表(
-
有效牌定义:
order = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]
- 明确顺子的合法牌面和顺序。
-
统计剩余牌数:
remain = {card: 4 - count.get(card, 0) for card in order}
- 计算每张牌剩余可用次数(初始为4,减去已出现的次数)。
-
滑动窗口遍历:
- 外层循环遍历每个可能的起始点
i
。 - 内层循环从
i
出发向右扩展窗口,直到遇到不可用的牌。 - 记录当前窗口的结束位置
current_end
。
- 外层循环遍历每个可能的起始点
-
最优顺子选择:
- 如果当前窗口长度大于历史最大值,更新结果。
- 如果长度相同,但结束索引更大(即牌面更大),也更新结果。
-
结果输出:
- 根据记录的起始和结束索引生成顺子字符串。
- 若没有合法顺子,输出
NO-CHAIN
。
示例测试
示例1输入:
3-3-4-4-5-A-5-6-2-8-3-9-10-Q-7-K-J-10-B
A-4-5-8-8-10-C-6-7-8
输出:
9-10-J-Q-K-A
解析:
- 剩余牌中
9,10,J,Q,K,A
连续且全部可用,长度为6,是唯一最长顺子。
示例2输入(无顺子):
3-3-3-3
4-4-4-4
输出:
NO-CHAIN
解析:
- 所有牌的剩余次数为0,无法组成顺子。
示例3输入(最长顺子):
3-4-5-6-7-8-9-10-J-Q-K-A
(空已出牌)
输出:
3-4-5-6-7-8-9-10-J-Q-K-A
解析:
- 所有牌均未被使用,可以组成完整的12张顺子。
综合分析
-
时间复杂度:
- 滑动窗口遍历:外层循环12次,内层最多遍历12次,复杂度为 (O(n^2)),其中 (n=12)。
- 总复杂度为 (O(144)),完全满足题目约束。
-
空间复杂度:
- 存储牌的顺序和剩余次数,空间复杂度为 (O(n)),其中 (n=12)。
-
优势:
- 严格保证最优解:通过滑动窗口直接遍历所有可能性。
- 高效剪枝:遇到不可用牌立即停止扩展,减少计算量。
-
适用场景:
- 题目明确要求牌数 (n < 31),滑动窗口法在小规模数据中表现最佳。
JavaScript
问题分析
我们需要找到对手可能组成的最长顺子。顺子由3到A的连续牌组成,不含2和大小王。输出最长顺子,若存在多个相同长度的,选择牌面最大的那个。若无法组成,返回"NO-CHAIN"。
解题思路
- 输入处理:读取手牌和已出牌,合并并分割为数组。
- 统计次数:计算每个有效牌的出现次数。
- 剩余计算:每张牌最多4张,剩余次数为
4 - 已用次数
。 - 滑动窗口遍历:从每个可能的起点向右扩展,找到最长连续可用序列。
- 结果选择:记录最长且最大的顺子。
代码实现
const readline = require('readline');const rl = readline.createInterface({input: process.stdin,output: process.stdout,terminal: false
});let lines = [];
rl.on('line', (line) => {lines.push(line.trim());
}).on('close', () => {// 解析输入的两行数据const hand = lines[0] ? lines[0].split('-') : [];const played = lines[1] ? lines[1].split('-') : [];const allCards = [...hand, ...played];// 定义有效牌的顺序和集合const order = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];const validCards = new Set(order);// 统计每张牌的出现次数const count = {};allCards.forEach(card => {if (validCards.has(card)) {count[card] = (count[card] || 0) + 1;}});// 计算剩余次数(4 - 已用次数)const remain = {};order.forEach(card => {remain[card] = 4 - (count[card] || 0);});let maxLength = 0; // 最长顺子的长度let bestStart = 0; // 最长顺子的起始索引let bestEnd = -1; // 最长顺子的结束索引// 滑动窗口遍历所有可能的起始点for (let i = 0; i < order.length; i++) {const startCard = order[i];if (remain[startCard] <= 0) continue; // 起始牌不可用则跳过let currentEnd = i;// 向右扩展窗口直到遇到不可用的牌for (let j = i; j < order.length; j++) {const currentCard = order[j];if (remain[currentCard] <= 0) break;currentEnd = j;}const currentLength = currentEnd - i + 1;// 更新最优解(长度优先,其次比较末尾牌大小)if (currentLength >= 5) {if (currentLength > maxLength ||(currentLength === maxLength && currentEnd > bestEnd)) {maxLength = currentLength;bestStart = i;bestEnd = currentEnd;}}}// 输出结果if (maxLength >= 5) {const bestChain = order.slice(bestStart, bestEnd + 1).join('-');console.log(bestChain);} else {console.log("NO-CHAIN");}
});
代码详细解析
-
输入处理:
- 使用
readline
读取输入,存储到lines
数组。 - 将手牌和已出牌分割为数组,合并到
allCards
。
- 使用
-
有效牌定义:
const order = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]; const validCards = new Set(order);
- 明确合法的顺子牌及其顺序。
-
统计次数:
allCards.forEach(card => {if (validCards.has(card)) {count[card] = (count[card] || 0) + 1;} });
- 遍历所有牌,统计有效牌的出现次数。
-
剩余计算:
order.forEach(card => {remain[card] = 4 - (count[card] || 0); });
- 计算每个牌的剩余次数(总次数为4)。
-
滑动窗口遍历:
- 外层循环遍历每个可能的起始点
i
。 - 内层循环从
i
出发向右扩展窗口,直到遇到剩余为0的牌。 - 记录窗口的结束位置
currentEnd
。
- 外层循环遍历每个可能的起始点
-
结果选择:
- 如果当前窗口长度大于历史最大值,更新最优解。
- 如果长度相同但末尾牌更大(即
currentEnd
更大),也更新最优解。
-
输出结果:
- 生成顺子字符串(如
9-10-J-Q-K-A
)或输出NO-CHAIN
。
- 生成顺子字符串(如
示例测试
示例1输入:
3-3-4-4-5-A-5-6-2-8-3-9-10-Q-7-K-J-10-B
A-4-5-8-8-10-C-6-7-8
输出:
9-10-J-Q-K-A
解析:
- 剩余牌中
9,10,J,Q,K,A
连续可用,形成最长顺子。
示例2输入(无顺子):
3-3-3-3
4-4-4-4
输出:
NO-CHAIN
解析:
- 所有牌剩余次数为0,无法组成顺子。
示例3输入(最长顺子):
3-4-5-6-7-8-9-10-J-Q-K-A
(空已出牌)
输出:
3-4-5-6-7-8-9-10-J-Q-K-A
综合分析
-
时间复杂度:
- 滑动窗口遍历:外层循环12次,内层最多12次,复杂度为 (O(n^2)),其中 (n=12)。
- 总复杂度为 (O(144)),完全满足题目约束。
-
空间复杂度:
- 存储牌的顺序和剩余次数,空间复杂度为 (O(n)),其中 (n=12)。
-
优势:
- 严格保证最优解:滑动窗口直接遍历所有可能性。
- 高效剪枝:遇到不可用牌立即停止扩展,减少计算量。
-
适用场景:
- 题目明确要求牌数 (n < 31),滑动窗口法在小规模数据中表现最佳。
C++
问题分析
我们需要找出对手可能组成的最长顺子。顺子由3到A的连续牌组成,不含2和大小王。输出最长顺子,若存在多个相同长度的,选择牌面最大的那个。若无法组成,返回"NO-CHAIN"。
解题思路
- 输入处理:读取手牌和已出牌,分割成数组。
- 统计次数:过滤无效牌,统计每张牌的出现次数。
- 剩余计算:每张牌最多4张,剩余次数为
4 - 已用次数
。 - 滑动窗口遍历:从每个可能的起点扩展窗口,找到最长连续可用序列。
- 结果生成:记录最长且最大的顺子并输出。
代码实现
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <sstream>
#include <unordered_set>using namespace std;// 分割字符串函数
vector<string> split(const string &s, char delimiter) {vector<string> tokens;string token;istringstream tokenStream(s);while (getline(tokenStream, token, delimiter)) {if (!token.empty()) {tokens.push_back(token);}}return tokens;
}// 连接字符串函数
string join(const vector<string> &v, const string &delimiter) {if (v.empty()) return "";string result = v[0];for (size_t i = 1; i < v.size(); ++i) {result += delimiter + v[i];}return result;
}int main() {// 定义有效牌的顺序const vector<string> order = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};unordered_set<string> validCards(order.begin(), order.end());// 读取输入string handInput, playedInput;getline(cin, handInput);getline(cin, playedInput);// 分割输入为牌列表vector<string> hand = split(handInput, '-');vector<string> played = split(playedInput, '-');vector<string> allCards;allCards.insert(allCards.end(), hand.begin(), hand.end());allCards.insert(allCards.end(), played.begin(), played.end());// 统计每张牌的出现次数(仅有效牌)unordered_map<string, int> countMap;for (const auto &card : allCards) {if (validCards.find(card) != validCards.end()) {countMap[card]++;}}// 计算剩余次数vector<int> remain(order.size(), 4);for (int i = 0; i < order.size(); ++i) {const string &card = order[i];remain[i] = 4 - countMap[card];}// 滑动窗口遍历寻找最长顺子int maxLen = 0;int bestStart = 0;int bestEnd = -1;for (int i = 0; i < order.size(); ++i) {if (remain[i] <= 0) continue; // 起始牌不可用则跳过int currentEnd = i;for (int j = i; j < order.size(); ++j) {if (remain[j] <= 0) break; // 遇到不可用的牌,停止扩展currentEnd = j; // 更新窗口结束位置}int currentLen = currentEnd - i + 1;// 更新最优解(优先长度,其次末尾牌大小)if (currentLen >= 5) {if (currentLen > maxLen || (currentLen == maxLen && currentEnd > bestEnd)) {maxLen = currentLen;bestStart = i;bestEnd = currentEnd;}}}// 输出结果if (maxLen >= 5) {vector<string> bestChain;for (int i = bestStart; i <= bestEnd; ++i) {bestChain.push_back(order[i]);}cout << join(bestChain, "-") << endl;} else {cout << "NO-CHAIN" << endl;}return 0;
}
代码详细解析
-
输入处理:
getline
读取输入的两行数据。split
函数按-
分割字符串,得到手牌和已出牌列表。
-
统计次数:
- 使用
unordered_map
统计有效牌的出现次数,过滤2和大小王。
- 使用
-
剩余计算:
- 数组
remain
存储每个有效牌的剩余次数,初始为4,减去已用次数。
- 数组
-
滑动窗口遍历:
- 外层循环遍历每个起始点
i
,若该牌剩余可用则继续。 - 内层循环向右扩展窗口,直到遇到剩余为0的牌,记录窗口结束位置。
- 比较当前窗口长度和记录的最大值,更新最优解。
- 外层循环遍历每个起始点
-
结果生成:
- 根据记录的起始和结束位置生成顺子字符串,用
join
函数连接。
- 根据记录的起始和结束位置生成顺子字符串,用
示例测试
示例1输入:
3-3-4-4-5-A-5-6-2-8-3-9-10-Q-7-K-J-10-B
A-4-5-8-8-10-C-6-7-8
输出:
9-10-J-Q-K-A
解析:剩余牌中 9-10-J-Q-K-A
是连续且最长的。
示例2输入(无顺子):
3-3-3-3
4-4-4-4
输出:
NO-CHAIN
示例3输入(完整顺子):
3-4-5-6-7-8-9-10-J-Q-K-A
(空已出牌)
输出:
3-4-5-6-7-8-9-10-J-Q-K-A
综合分析
-
时间复杂度:
- 滑动窗口遍历:外层循环12次,内层最多12次,复杂度为 (O(n^2)),其中 (n=12)。
- 总时间 (O(144)),完全满足题目时间限制。
-
空间复杂度:
- 存储牌的顺序和剩余次数,空间复杂度为 (O(n)),其中 (n=12)。
-
优势:
- 严格最优解:滑动窗口遍历所有可能性,保证找到最长且最大的顺子。
- 高效剪枝:遇到不可用牌立即停止扩展,减少无效计算。
-
适用场景:
- 题目约束的输入规模(牌数 < 31),高效处理中小规模数据。
C语言
问题分析
我们需要找到对手可能组成的最长顺子,顺子由3到A的连续牌组成,不含2和大小王。输出最长顺子,若存在多个相同长度的,选择牌面最大的那个。若无法组成,返回"NO-CHAIN"。
解题思路
- 输入处理:读取手牌和已出牌,分割为数组。
- 统计剩余牌数:计算每个有效牌的剩余数量(总4张减去已出现次数)。
- 滑动窗口遍历:从每个可能的起点向右扩展,寻找最长连续可用序列。
- 结果选择:记录最长且末尾最大的顺子。
代码实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#define MAX_CARDS 1000 // 最大输入牌数
#define ORDER_SIZE 12 // 有效牌种类数// 有效牌顺序:3,4,5,6,7,8,9,10,J,Q,K,A
const char *order[] = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};// 分割字符串为数组,返回分割后的元素数量
int split_string(char *str, char result[][4], const char *delim) {int count = 0;char *token = strtok(str, delim);while (token != NULL) {strcpy(result[count], token);count++;token = strtok(NULL, delim);}return count;
}// 根据牌名查找其索引,返回-1表示无效牌
int find_index(const char *card) {for (int i = 0; i < ORDER_SIZE; i++) {if (strcmp(card, order[i]) == 0) return i;}return -1;
}int main() {char hand[1000], played[1000];char cards_hand[MAX_CARDS][4], cards_played[MAX_CARDS][4];int hand_count = 0, played_count = 0;// 读取输入并分割fgets(hand, sizeof(hand), stdin);hand[strcspn(hand, "\n")] = '\0'; // 去除换行符fgets(played, sizeof(played), stdin);played[strcspn(played, "\n")] = '\0';hand_count = split_string(hand, cards_hand, "-");played_count = split_string(played, cards_played, "-");// 统计所有牌的出现次数int count[ORDER_SIZE] = {0};for (int i = 0; i < hand_count; i++) {int idx = find_index(cards_hand[i]);if (idx != -1) count[idx]++;}for (int i = 0; i < played_count; i++) {int idx = find_index(cards_played[i]);if (idx != -1) count[idx]++;}// 计算剩余次数int remain[ORDER_SIZE];for (int i = 0; i < ORDER_SIZE; i++) {remain[i] = 4 - count[i];}// 滑动窗口遍历寻找最长顺子int max_len = 0, best_start = 0, best_end = -1;for (int i = 0; i < ORDER_SIZE; i++) {if (remain[i] <= 0) continue; // 起始牌不可用int current_end = i;for (int j = i; j < ORDER_SIZE; j++) {if (remain[j] <= 0) break;current_end = j;}int current_len = current_end - i + 1;if (current_len >= 5) {if (current_len > max_len || (current_len == max_len && current_end > best_end)) {max_len = current_len;best_start = i;best_end = current_end;}}}// 输出结果if (max_len >= 5) {for (int i = best_start; i <= best_end; i++) {printf("%s", order[i]);if (i != best_end) printf("-");}printf("\n");} else {printf("NO-CHAIN\n");}return 0;
}
代码详细解析
-
定义常量:
MAX_CARDS
:输入牌的最大数量。ORDER_SIZE
:有效牌的种类数(3到A共12种)。
-
有效牌顺序:
const char *order[] = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
- 明确有效牌的顺序,方便后续处理。
-
分割字符串:
int split_string(char *str, char result[][4], const char *delim) {// 使用strtok分割字符串为数组 }
- 将输入行按“-”分割为单独的牌。
-
查找牌索引:
int find_index(const char *card) {for (int i = 0; i < ORDER_SIZE; i++) {if (strcmp(card, order[i]) == 0) return i;}return -1; }
- 根据牌名查找其在顺序数组中的索引,用于统计次数。
-
统计剩余次数:
int remain[ORDER_SIZE]; for (int i = 0; i < ORDER_SIZE; i++) {remain[i] = 4 - count[i]; }
- 计算每张牌的剩余可用次数。
-
滑动窗口遍历:
- 外层循环遍历所有可能的起始点。
- 内层循环向右扩展窗口,直到遇到不可用牌。
- 记录最长且最大的窗口。
-
输出结果:
- 生成顺子字符串或输出"NO-CHAIN"。
示例测试
示例1输入:
3-3-4-4-5-A-5-6-2-8-3-9-10-Q-7-K-J-10-B
A-4-5-8-8-10-C-6-7-8
输出:
9-10-J-Q-K-A
解析:
- 剩余牌中9到A连续且可用,形成最长顺子。
示例2输入(无顺子):
3-3-3-3
4-4-4-4
输出:
NO-CHAIN
解析:
- 所有牌剩余次数为0,无法组成顺子。
示例3输入(最长顺子):
3-4-5-6-7-8-9-10-J-Q-K-A
(空已出牌)
输出:
3-4-5-6-7-8-9-10-J-Q-K-A
综合分析
-
时间复杂度:
- 输入处理:O(n),n为输入牌数量。
- 滑动窗口遍历:O(12²) = O(144),高效处理小规模数据。
-
空间复杂度:
- 输入存储:O(n),存储分割后的牌。
- 统计数组:O(12),固定空间消耗。
-
优势:
- 严格顺序遍历:保证找到最长且最大的顺子。
- 高效剪枝:遇到不可用牌立即停止扩展。
-
适用场景:
- 输入规模小(题目约束),适合滑动窗口法。
GO
问题分析
我们需要找到对手可能组成的最长顺子。顺子由3到A的连续牌组成,不含2和大小王。输出最长顺子,若存在多个相同长度的,选择牌面最大的那个。若无法组成,返回"NO-CHAIN"。
解题思路
- 输入处理:读取手牌和已出牌,分割为数组并过滤无效牌。
- 统计剩余牌数:每张牌最多4张,剩余次数为
4 - 已用次数
。 - 滑动窗口遍历:从每个可能的起点向右扩展,寻找最长连续可用序列。
- 结果选择:记录最长且末尾最大的顺子。
代码实现
package mainimport ("bufio""fmt""os""strings"
)func main() {scanner := bufio.NewScanner(os.Stdin)// 读取手牌和已出牌var hand, played stringif scanner.Scan() {hand = scanner.Text()}if scanner.Scan() {played = scanner.Text()}// 合并并分割所有牌allCards := append(strings.Split(hand, "-"),strings.Split(played, "-")...,)// 有效牌顺序order := []string{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}validCards := make(map[string]bool)for _, card := range order {validCards[card] = true}// 统计每张牌的出现次数count := make(map[string]int)for _, card := range allCards {if validCards[card] {count[card]++}}// 计算剩余次数remain := make(map[string]int)for _, card := range order {remain[card] = 4 - count[card]}maxLength := 0 // 最长顺子的长度bestStart := 0 // 起始索引bestEnd := -1 // 结束索引// 滑动窗口遍历所有可能的起始点for i := 0; i < len(order); i++ {if remain[order[i]] <= 0 {continue // 起始牌不可用则跳过}currentEnd := i // 当前窗口的结束位置// 向右扩展窗口直到遇到不可用的牌for j := i; j < len(order); j++ {if remain[order[j]] <= 0 {break}currentEnd = j}currentLength := currentEnd - i + 1// 更新最优解(长度优先,其次比较结束位置)if currentLength >= 5 {if currentLength > maxLength || (currentLength == maxLength && currentEnd > bestEnd) {maxLength = currentLengthbestStart = ibestEnd = currentEnd}}}// 输出结果if maxLength >= 5 {chain := strings.Join(order[bestStart:bestEnd+1], "-")fmt.Println(chain)} else {fmt.Println("NO-CHAIN")}
}
代码详细解析
-
输入处理:
- 使用
bufio.Scanner
读取输入的两行数据。 - 合并手牌和已出牌,分割为数组
allCards
。
- 使用
-
有效牌定义:
order := []string{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}
- 明确有效牌的顺序,用
map
快速判断合法性。
- 明确有效牌的顺序,用
-
统计剩余次数:
remain := make(map[string]int) for _, card := range order {remain[card] = 4 - count[card] }
- 计算每张牌的剩余次数(4 - 已用次数)。
-
滑动窗口遍历:
- 外层循环遍历每个可能的起始点
i
。 - 内层循环从
i
向右扩展,直到遇到剩余为0的牌。 - 记录窗口的结束位置
currentEnd
。
- 外层循环遍历每个可能的起始点
-
结果选择:
- 如果当前窗口长度大于历史最大值,或长度相同但结束位置更大,则更新最优解。
-
输出结果:
- 生成顺子字符串或输出
NO-CHAIN
。
- 生成顺子字符串或输出
示例测试
示例1输入:
3-3-4-4-5-A-5-6-2-8-3-9-10-Q-7-K-J-10-B
A-4-5-8-8-10-C-6-7-8
输出:
9-10-J-Q-K-A
解析:剩余牌中 9,10,J,Q,K,A
连续可用,形成最长顺子。
示例2输入(无顺子):
3-3-3-3
4-4-4-4
输出:
NO-CHAIN
解析:所有牌剩余次数为0,无法组成顺子。
示例3输入(最长顺子):
3-4-5-6-7-8-9-10-J-Q-K-A
(空已出牌)
输出:
3-4-5-6-7-8-9-10-J-Q-K-A
综合分析
-
时间复杂度:
- 输入处理:O(n),n为输入牌数量。
- 滑动窗口遍历:O(12²) = O(144),高效处理小规模数据。
-
空间复杂度:
- 统计存储:O(12),存储剩余次数和牌顺序。
-
优势:
- 严格顺序遍历:保证找到最长且最大的顺子。
- 高效剪枝:遇到不可用牌立即停止扩展。
-
适用场景:
- 输入规模小(题目约束),适合滑动窗口法。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!
相关文章:
华为OD机试真题——最长的顺子(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录全流程解析/备考攻略/经验…...
3.8/Q1,GBD数据库最新文章解读
文章题目:Regional and National Burden of Traumatic Brain Injury and Spinal Cord Injury in North Africa and Middle East Regions, 1990-2021: A Systematic Analysis for The Global Burden of Disease Study 2021 DOI:10.1007/s44197-025-00372-…...
Java面试中问单例模式如何回答
1. 什么是单例模式? 单例模式(Singleton Pattern)是一种设计模式,确保某个类在整个应用中只有一个实例,并且提供全局访问点。它有以下特点: 确保只有一个实例。提供全局访问点。防止多次实例化࿰…...
再看开源多模态RAG的视觉文档(OCR-Free)检索增强生成方案-VDocRAG
前期几个工作提到,基于OCR的文档解析RAG的方式进行知识库问答,受限文档结构复杂多样,各个环节的解析泛化能力较差,无法完美的对文档进行解析。因此出现了一些基于多模态大模型的RAG方案。如下: 【RAG&多模态】多模…...
【go】什么是Go语言中的GC,作用是什么?调优,sync.Pool优化,逃逸分析演示
Go 语言中的 GC 简介与调优建议 一、GC 简介 Go 的 GC(Garbage Collection)用于自动管理内存,开发者无需手动释放内存,可以专注于业务逻辑,降低出错概率,提升开发效率。 GC 能够自动发现和回收不再使用的…...
ctfshow-大赛原题-web702
因为该题没有理解到位,导致看wp也一直出错,特此反思一下。 参考yu22x师傅的文章 :CTFSHOW大赛原题篇(web696-web710)_ctfshow 大赛原题-CSDN博客 首先拿到题目: // www.zip 下载源码 我们的思路就是包含一个css文件,…...
基于Redis的4种延时队列实现方式
延时队列是一种特殊的消息队列,它允许消息在指定的时间后被消费。在微服务架构、电商系统和任务调度场景中,延时队列扮演着关键角色。例如,订单超时自动取消、定时提醒、延时支付等都依赖延时队列实现。 Redis作为高性能的内存数据库&#x…...
基于Django实现农业生产可视化系统
基于Django实现农业生产可视化系统 项目截图 登录 注册 首页 农业数据-某一指标表格展示 农业数据-某一指标柱状图展示 农业数据-某一指标饼状图展示 气候数据-平均气温地图展示 气候数据-降水量合并图展示 后台管理 一、系统简介 农业生产可视化系统是一款基于DjangoMVTMyS…...
yocto编译使用共享缓存
注意 服务器端与客户端系统的版本号需为Ubuntu20.04执行用户需要为sudo权限服务器端nfs目录权限必须为nobody:nogroup 服务端配置: 在服务器192.168.60.142上配置 NFS 共享: 1.安装 NFS 服务器: 1 sudo apt-get install nfs-kernel-serve…...
uCOS3实时操作系统(系统架构和中断管理)
文章目录 系统架构中断管理ARM中断寄存器相关知识ucos中断机制 系统架构 ucos主要包含三个部分的源码: 1、OS核心源码及其配置文件(ucos源码) 2、LIB库文件源码及其配置文件(库文件,比如字符处理、内存管理࿰…...
web后端语言下篇
#作者:允砸儿 #日期:乙巳青蛇年 三月廿一 笔者今天将web后端语言PHP完结一下,后面还会写一个关于python的番外。 PHP函数 PHP函数它和笔者前面写的js函数有些许类似,都是封装的概念。将实现某一功能的代码块封装到一个结构中…...
Tensorflow释放GPU资源
语言:python 框架:tensorflow 现有问题:用tensorflow进行模型训练,训练完成后用tf.keras.backend.clear_session()命令无法真正实现释放资源的效果。 解决方案:创建多进程,将模型训练作为子进程,…...
vscode、cherry studio接入高德mcp服务
最近mcp协议比较火,好多平台都已经开通了mcp协议,今天来接入下高德的mcp看看效果如何。 话不多说,咱们直接开干。 先来看下支持mcp协议的工具有cusor、cline等等。更新cherrystudio后发现上面也有mcp服务器了。今天咱就来试试添加高德的mcp协…...
软考高级-系统架构设计师 论文范文参考(二)
文章目录 论企业应用集成论软件三层结构的设计论软件设计模式的应用论软件维护及软件可维护性论信息系统安全性设计论信息系统的安全性设计(二)论信息系统的架构设计论信息系统架构设计(二) 论企业应用集成 摘要: 2016年9月,我国某省移动通信有限公司决定启动VerisB…...
熵权法+TOPSIS+灰色关联度综合算法(Matlab实现)
熵权法TOPSIS灰色关联度综合算法(Matlab实现) 代码获取私信回复:熵权法TOPSIS灰色关联度综合算法(Matlab实现) 摘要: 熵权法TOPSIS灰色关联度综合算法(Matlab实现)代码实现了一种…...
【java 13天进阶Day05】数据结构,List,Set ,TreeSet集合,Collections工具类
常见的数据结构种类 集合是基于数据结构做出来的,不同的集合底层会采用不同的数据结构。不同的数据结构,功能和作用是不一样的。数据结构: 数据结构指的是数据以什么方式组织在一起。不同的数据结构,增删查的性能是不一样的。不同…...
极狐GitLab 议题和史诗创建的速率限制如何设置?
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 议题和史诗创建的速率限制 (BASIC SELF) 速率限制是为了控制新史诗和议题的创建速度。例如,如果您将限制设置为 …...
AIGC-几款本地生活服务智能体完整指令直接用(DeepSeek,豆包,千问,Kimi,GPT)
Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列AIGC(GPT、DeepSeek、豆包、千问、Kimi)👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资…...
十、数据库day02--SQL语句01
文章目录 一、新建查询1.查询窗口的开启方法2. 单语句运行方法 二、数据库操作1.创建数据库2. 使用数据库3. 修改数据库4. 删除数据库和查看所有数据库5. 重点:数据库备份5.1 应用场景5.2 利用工具备份备份操作还原操作 5.3 扩展:使用命令备份 三、数据表…...
2025年MathorCup数学应用挑战赛D题问题一求解与整体思路分析
D题 短途运输货量预测及车辆调度 问题背景 问题分析:四个问题需要建立数学模型解决就业状态分析与预测,旨在通过数学建模对宜昌地区的就业数据进行深入分析,并基于此预测就业状态。提供的数据涵盖了被调查者的个人信息、就业信息、失业信息…...
关于三防漆清除剂
成分及原理 主要成分:通常包含有机溶剂,如丙酮、甲苯、二甲苯等,以及一些表面活性剂、缓蚀剂等添加剂。工作原理:有机溶剂能够溶解三防漆中的树脂等成分,使其失去粘性和附着性,从而可以被轻易地擦拭或冲洗…...
2025年MathorCup数学应用挑战赛【选题分析】
【25MathorCup选题分析】 🙋♀🙋♂数模加油站初步分析评估了此次竞赛题目: ✅A题:该题新颖性强,属于“算子学习深度学习几何建模”的交叉问题,涉及PINN、FNO、KAN等算子神经网络模型构建,任…...
在windows上交叉编译opencv供RK3588使用
环境 NDK r27、RK3588 安卓板子、Android 12 步骤操作要点1. NDK 下载选择 r27 版本,解压到无空格路径(如 C:/ndk)2. 环境变量配置添加 ANDROID_NDK_ROOT 和工具链路径到系统 PATH3. CMake 参数调整指定 ANDROID_NATIVE_API_LEVEL31、ANDRO…...
零基础玩转AI数学建模:从理论到实战
前言 数学建模作为连接数学理论与现实世界的桥梁,在科学研究、工程实践和商业决策等领域发挥着越来越重要的作用。随着人工智能技术的迅猛发展,以ChatGPT为代表的大语言模型为数学建模领域带来了革命性的变革。本书旨在帮助读者掌握这一变革带来的新机遇…...
IDEA 2025.1更新-AI助手试用和第三方模型集成方案
今天刚把 IntelliJ IDEA 更新到了 2025.1 版本,主要是想看看这次 AI Assistant 有什么新东西。之前看到消息说功能有更新,而且似乎可以免费试用,就动手试了试,顺便把过程和一些发现记录下来,给可能需要的朋友一个参考。…...
static关键字
思维导图: 在 Java 中,static 是一个非常重要的关键字,它可以用来修饰类的成员,包括变量、方法、代码块以及内部类。下面为你详细介绍 static 关键字的各种用法和特点。 一.修饰内部类 静态内部类:当 static 修饰内部类…...
gl-matrix 库简介
gl-matrix 库简介 gl-matrix 是一个高性能的 JavaScript 矩阵和向量库,专门为 WebGL 和其他 3D 图形应用设计。它提供了处理 2D、3D 和 4D 向量以及矩阵运算的高效方法。 主要特性 高性能:经过高度优化,执行速度快轻量级:体积小…...
Spring Boot 核心注解全解:@SpringBootApplication背后的三剑客
大家好呀!👋 今天我们要聊一个超级重要的Spring Boot话题 - 那个神奇的主类注解SpringBootApplication!很多小伙伴可能每天都在用Spring Boot开发项目,但你真的了解这个注解背后的秘密吗?🤔 别担心&#x…...
Android 音频架构全解析:从 AudioTrack 到 AudioFlinger
在开发音视频相关应用时,我们常会接触到 MediaPlayer、SoundPool、AudioTrack、OpenSL ES、AAudio、Oboe 等名词,它们都与 Android 的音频播放息息相关。然而,真正理解它们之间的关系以及背后运行机制,才能写出高性能、低延迟的音…...
【教程】无视硬件限制强制升级Windows 11
转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 1、下载升级工具:https://github.com/builtbybel/Flyby11/releases 2、解压后打开软件: 3、拖入win11.iso或者自动下载…...
ICPR-2025 | 让机器人在未知环境中 “听懂” 指令精准导航!VLTNet:基于视觉语言推理的零样本目标导航
作者:Congcong Wen, Yisiyuan Huang, Hao Huang ,Yanjia Huang, Shuaihang Yuan, YuHao, HuiLin and Yi Fang 单位:纽约大学阿布扎比分校具身人工智能与机器人实验室,纽约大学阿布扎比分校人工智能与机器人中心,纽约大学坦登工程…...
替代升级VMware | 云轴科技ZStack构建山西证券一云多芯云平台
通过云轴科技ZStack Cloud云平台,山西证券打造了敏捷部署、简单运维的云平台,不仅兼容x86、海光、鲲鹏三种异构服务器实现一云多芯,还通过云平台虚拟化纳管模块纳管原有VMware虚拟化资源,并对接第三方集中式存储,在保护…...
Houdini python code:参数指定文件路径
创建null节点并命名为control并增加filedir参数 创建python节点 node hou.pwd() geo node.geometry()node hou.node(/obj/output_tetgen/control) filedir node.parm(filedir).eval() print("filedir:",filedir)得到输出...
ChatGPT-o3辅助学术写作的关键词和引言效果如何?
目录 关键词 引言 论文引言(≈300 字) 大家好这里是AIWritePaper官方账号,官网👉AIWritePaper~ 关键词 摘要是文章的精华,通常在200-250词左右。要包括研究的目的、方法、结果和结论。让AI工具作为某领域内资深的研…...
树莓派5+Vosk+python实现语音识别
简介 Vosk是语音识别开源框架,支持二十种语言 - 中文,英语,印度英语,德语,法语,西班牙语,葡萄牙语,俄语,土耳其语,越南语,意大利语,荷…...
Selenium之 CSS 选择器详细讲解
Selenium之 CSS 选择器详细讲解 引言 在.Selenium.自动化测试中,元素定位是至关重要的一环。而.CSS.选择器作为一种强大且灵活的定位工具,在.Selenium.中得到了广泛的应用。本文将详细介绍.CSS.选择器的基本语法、常用类型以及如何在.Selenium.中高效地…...
【LeetCode】大厂面试算法真题回忆(61)--组装新的数组
题目描述 给你一个整数M和数组N,N中的元素为连续整数,要求根据N中的元素组装成新的数组R,组装规则: R中元素总和加起来等于M。R中的元素可以从N中重复选取。R中的元素最多只能有1个不在N中,且比N中的数字都要小(不能为负数)。请输出:数组R一共有多少组装办法。 输入描…...
基于用户的协同过滤推荐系统实战项目
文章目录 基于用户的协同过滤推荐系统实战项目1. 推荐系统基础理论1.1 协同过滤概述1.2 基于用户的协同过滤原理1.3 相似度计算方法1.3.1 余弦相似度(Cosine Similarity)1.3.2 皮尔逊相关系数(Pearson Correlation)1.3.3 欧几里得距离(Euclidean Distance)1.3.4 调整余弦相似度…...
浅析数据库面试问题
以下是关于数据库的一些常见面试问题: 一、基础问题 什么是数据库? 数据库是按照数据结构来组织、存储和管理数据的仓库。SQL 和 NoSQL 的区别是什么? SQL 是关系型数据库,使用表结构存储数据;NoSQL 是非关系型数据库,支持多种数据模型(如文档型、键值对型等)。什么是…...
【Python3】Django 学习之路
第一章:Django 简介 1.1 什么是 Django? Django 是一个高级的 Python Web 框架,旨在让 Web 开发变得更加快速和简便。它鼓励遵循“不要重复自己”(DRY,Don’t Repeat Yourself)的原则,并提供了…...
Java并发编程高频面试题(已整理Java面试宝典PDF完整版)
为什么要使用并发编程 提升多核CPU利用率:现代计算机通常配备多核CPU,通过创建多个线程,操作系统可以将不同线程分配到不同CPU核心上并行执行,从而充分利用硬件资源。若仅使用单线程,则只能利用一个CPU核心,…...
第 4 期:DDPM中的损失函数——为什么只预测噪声?
—— 从变分下界到噪声预测 回顾:我们到底在做什么? 在第 3 期中,我们介绍了扩散模型的逆过程建模。简而言之,目标是通过神经网络学习从噪声 x_t 中恢复图像 x_0,并且我们通过预测噪声 ϵ来完成这个任务。 今天&a…...
Docker使用、容器迁移
Docker 简介 Docker 是一个开源的容器化平台,用于打包、部署和运行应用程序及其依赖环境。Docker 容器是轻量级的虚拟化单元,运行在宿主机操作系统上,通过隔离机制(如命名空间和控制组)确保应用运行环境的一致性和可移…...
专业热度低,25西电光电工程学院(考研录取情况)
1、光电工程学院各个方向 2、光电工程学院近三年复试分数线对比 学长、学姐分析 由表可看出: 1、光学工程25年相较于24年下降20分, 2、光电信息与工程(专硕)25年相较于24年上升15分 3、25vs24推免/统招人数对比 学长、学姐分析…...
六、LangChain Agent 最佳实践
1. 架构设计与组件选择 (1) 核心组件分层设计 Model(LLM驱动层) 生产环境推荐:使用 gpt-4-1106-preview 或 Anthropic Claude 3 等高性能模型,结合 model.with_fallbacks() 实现故障转移(如备用模型或本地模型)。本地部署:选择 Llama3-70B 等开源模型,搭配 Docker 或 …...
ubantu18.04(Hadoop3.1.3)之MapReduce编程
说明:本文图片较多,耐心等待加载。(建议用电脑) 注意所有打开的文件都要记得保存。 第一步:准备工作 本文是在之前Hadoop搭建完集群环境后继续进行的,因此需要读者完成我之前教程的所有操作。 第二步&…...
PoCL环境搭建
PoCL环境搭建 **一.关键功能与优势****二.设计目的****三.测试步骤**1.创建容器2.安装依赖3.编译安装pocl4.运行OpenCL测试程序 Portable Computing Language (PoCL) 简介 Portable Computing Language (PoCL) 是一个开源的、符合标准的异构计算框架,旨在为 OpenCL…...
关于hadoop和yarn的问题
1.hadoop的三大结构及各自的作用? HDFS(Hadoop Distributed File System):分布式文件系统,负责海量数据的存储,具有高容错性和高吞吐量。 MapReduce:分布式计算框架,用于并行处理大…...
软件工程中数据一致性的探讨
软件工程中数据一致性的探讨 引言数据一致性:软件工程中的业务正确性与性能的权衡数据一致性为何重要业务正确性:事务的原子性与一致性ACID原则的基石分布式事务的挑战一致性级别:从强一致到最终一致 实践中的一致性权衡金融系统:…...
在服务器上安装redis
1.安装所需插件gcc 查看gcc版本 gcc -v 没有安装的话,安装命令如下 yum -y install gcc 2.安装 下载安装包 https://download.redis.io/releases/ 将安装包上传到/opt/software目录下 解压安装包 cd /opt/software tar -zxvf redis-6.2.6.tar.gz 编译并安装redis到指…...