- 教程
- 文档
【免费下载链接】LogicStack-LeetCode
公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码
本篇技术指南以 LogicStack-LeetCode 仓库中的题解 1773. 统计匹配检索规则的物品数量 为骨架,完整讲解该「简单」题目的匹配规则、模拟思路与 Java / TypeScript / Python 多语言实现,并补充 C++ 变体、可读性优先写法及易错点分析。读完本文,你将掌握一类「按规则字段检索二维数据」问题的通用处理手法,也能理解为何在题目约束下可以用首字符快速完成规则到列下标的映射。
题目描述与匹配规则
给定一个二维数组items,其中每一项items[i] = [type_i, color_i, name_i]依次描述第i件物品的类型、颜色、名称。
另给出一条检索规则,由两个字符串ruleKey和ruleValue组成。当且仅当满足下列条件之一时,物品i被视为匹配该规则:
ruleKey = "type"且ruleValue = type_i;ruleKey = "color"且ruleValue = color_i;ruleKey = "name"且ruleValue = name_i。
题目要求统计并返回匹配检索规则的物品数量。
数据范围(题目提示)
| 项目 | 范围 |
|---|---|
items.length | 1 <= items.length <= 10^4 |
| 字段长度 | 1 <= type_i.length, color_i.length, name_i.length, ruleValue.length <= 10 |
ruleKey | 取值仅为"type"、"color"或"name" |
| 字符集 | 所有字符串仅由小写字母组成 |
这三条约束决定了我们可以采用极其轻量的实现:ruleKey只有三种固定取值,且三者的首字符t、c、n互不相同,为「首字符映射下标」的写法提供了前提。
示例走读
示例 1:
输入:items = [["phone","blue","pixel"],["computer","silver","lenovo"],["phone","gold","iphone"]] ruleKey = "color", ruleValue = "silver" 输出:1ruleKey = "color",因此只比较每件物品的第 2 个字段(下标 1)。三件物品中只有["computer","silver","lenovo"]的第 2 个字段等于"silver",故答案为1。
示例 2:
输入:items = [["phone","blue","pixel"],["computer","silver","phone"],["phone","gold","iphone"]] ruleKey = "type", ruleValue = "phone" 输出:2ruleKey = "type",只比较每件物品的第 1 个字段(下标 0)。["computer","silver","phone"]的name(下标 2)虽然也是"phone",但比较的字段是type,因此不匹配。匹配的只有["phone","blue","pixel"]和["phone","gold","iphone"],答案为2。
示例 2 特意提醒我们:匹配必须发生在ruleKey指定的那一个字段上,其它字段内容相同并不会产生匹配。
解题思路:把 ruleKey 映射为列下标
本题属于最经典的「模拟」题型——题意本身即算法,直接按规则逐条执行即可。
关键点在于:ruleKey是字符串,而items[i]是三元组,我们无法直接用ruleKey作为下标访问。因此第一步是把规则字符串翻译成一个列下标:
"type"→ 下标0"color"→ 下标1"name"→ 下标2
映射完成后,问题退化为一次线性扫描:对每个item,判断item[映射下标]是否等于ruleValue,相等则计数加一。
为什么可以用「首字符」完成映射
根据题目提示,ruleKey的取值被严格限定为"type"、"color"、"name"三者之一。三个单词的首字符分别是t、c、n,两两不同,因此只观察ruleKey的首字符就能唯一确定列下标:
首字符 't' → 下标 0(type) 首字符 'c' → 下标 1(color) 其余('n')→ 下标 2(name)这是典型的「利用题目约束做最简实现」:把三路if-else压缩成一个三元表达式。需要强调的是,该技巧成立的前提正是题目对ruleKey取值的硬约束;如果ruleKey可能是任意字符串,就必须改用显式的分支判断或哈希映射(本文后续会给出可读性优先的写法)。
多语言实现
以下实现均来自原题解,完整保留在仓库的 1773. 统计匹配检索规则的物品数量 中。
Java
class Solution { public int countMatches(List<List<String>> items, String k, String v) { int ans = 0, idx = k.charAt(0) == 't' ? 0 : k.charAt(0) == 'c' ? 1 : 2; for (List<String> item : items) { if (item.get(idx).equals(v)) ans++; } return ans; } }实现要点:
k.charAt(0)取出ruleKey首字符,配合嵌套三元表达式完成idx的映射;- 字符串比较必须使用
equals(v)而非==,因为比较的是内容而非引用地址; - 单次遍历,无额外数据结构。
TypeScript
function countMatches(items: string[][], k: string, v: string): number { let ans = 0, idx = k[0] == 't' ? 0 : k[0] == 'c' ? 1 : 2 for (const item of items) { if (item[idx] == v) ans++ } return ans }TS 中string[][]与 Java 的List<List<String>>一一对应,k[0]取首字符、item[idx]按下标取值,逻辑完全一致。
Python
class Solution: def countMatches(self, items: List[List[str]], k: str, v: str) -> int: ans, idx = 0, 0 if k[0] == 't' else 1 if k[0] == 'c' else 2 for item in items: if item[idx] == v: ans += 1 return ansPython 的连续三元表达式0 if ... else 1 if ... else 2与 Java 的嵌套三元写法等价,逐层缩进后阅读性反而更清晰。
C++(同思路扩展)
思路与上述实现完全同构,可作为本地调试时的对照版本:
class Solution { public: int countMatches(vector<vector<string>>& items, string k, string v) { int ans = 0; int idx = k[0] == 't' ? 0 : k[0] == 'c' ? 1 : 2; for (const auto& item : items) { if (item[idx] == v) ans++; } return ans; } };可读性优先的写法(哈希映射版)
若面试或工程场景下追求「一眼可读」,也可以用显式映射替代首字符技巧——代价是增加一次常数级的查表,复杂度不变:
class Solution { public int countMatches(List<List<String>> items, String k, String v) { Map<String, Integer> map = new HashMap<>(); map.put("type", 0); map.put("color", 1); map.put("name", 2); int idx = map.get(k); int ans = 0; for (List<String> item : items) { if (item.get(idx).equals(v)) ans++; } return ans; } }两种写法的选择标准很简单:代码最短(竞赛/刷题)选首字符映射,可读性与健壮性优先(工程/协作)选显式映射。
复杂度分析
- 时间复杂度:O(n),其中
n = items.length。规则映射为常数操作,随后仅需一次线性扫描,每件物品进行一次 O(1) 的取值与字符串比较。 - 空间复杂度:O(1)。除返回答案的计数器外不申请额外空间,无论采用首字符映射还是常数大小的哈希表(
Map大小恒为 3),均不随输入规模增长。
边界情况与易错点
字符串比较方式:Java 中必须用
equals;Python / TypeScript / C++ 中==对字符串即比较内容,可直接使用。若在 Java 中误用==,只有当ruleValue恰好是常量池中的同一对象时才可能成立,属于典型的隐蔽错误。比较字段的定位:
ruleKey决定的是「按哪个字段比较」,与ruleValue的内容无关。示例 2 中某物品的name等于"phone"但type不等于,ruleKey = "type"时就不匹配。首字符映射的前提:该技巧依赖「
ruleKey只可能是type/color/name三者之一」这一提示。若题目约束发生变化,应回退到switch/if-else/ 哈希映射等显式方式。重复字段值:同一
ruleValue可能对应多件物品(如示例 2),计数时逐件累加即可,无需去重——题目统计的是「物品数量」。
题型定位与仓库索引
本题在仓库的题型分类中被标记为「模拟」,见 Index/模拟.md(第 1773 条记录)。模拟类题目的共同特征是:状态转换规则由题意直接给出,实现时忠实还原规则、避免过度设计。
仓库中同属「模拟」且思路相近的简单题还可对照练习:
- K 次取反后最大化的数组和:按规则反复取反,属于带贪心色彩的模拟;
- 奇数值单元格的数目:按行/列增量规则模拟,进阶版可用位运算压缩空间;
- 比赛中的配对次数:按轮次配对规则模拟,并可抽象出
n - 1的数学结论。
- 比赛中的配对次数:按轮次配对规则模拟,并可抽象出
本仓库 README.md 说明这是一个「日更」的算法仓库,题解按题号归档在LeetCode/目录下,同时以Index/下的分类索引(如 模拟)组织全部题目,适合按题型刷穿。
小结
「统计匹配检索规则的物品数量」是一道难度为简单的纯模拟题,它的价值在于两点:一是训练「把字符串规则翻译为可计算下标」的建模能力;二是提醒我们善用题目约束写出更简洁的代码——在ruleKey取值受限的前提下,首字符三元映射可以把三路分支压缩到一行,同时将整体复杂度维持在 O(n) 时间、O(1) 空间。
掌握这一题之后,遇到「按条件字段检索记录」「按枚举名定位列下标」之类的模拟题,都可以复用同一套思路:先建模(规则 → 下标/索引),再扫描(逐条比对计数),最后利用约束做最小化实现。
- 教程
- 文档
【免费下载链接】LogicStack-LeetCode
公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码
相关推荐
LogicStack-LeetCode 题解精讲:动态规划攻克 LeetCode 10「正则表达式匹配」的完整推导与多语言实现
LogicStack LeetCode 题解精讲:动态规划攻克 LeetCode 10「正则表达式匹配」的完整推导与多语言实现 本文是「宫水三叶的刷题日记」系列
教程文档LeetCode 1047 题解:删除字符串中的所有相邻重复项——栈与数组模拟的多种实现(LogicStack-LeetCode 刷题笔记)
LeetCode 1047 题解:删除字符串中的所有相邻重复项——栈与数组模拟的多种实现(LogicStack LeetCode 刷题笔记) 导读 本文围绕 L
教程文档LogicStack-LeetCode 刷题笔记:双指针与通用解法吃透数组移除元素问题(LeetCode 26 / 27)
LogicStack LeetCode 刷题笔记:双指针与通用解法吃透数组移除元素问题(LeetCode 26 / 27) 本篇技术指南围绕公众号「宫水三叶的刷
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考