为你整理 LeetCode 1419「数青蛙」两种解法的对比笔记:
📝 LeetCode 1419 数青蛙 - 解法笔记
核心思路 :状态机计数。青蛙叫必须依序输出 c -> r -> o -> a -> k。用数组模拟哈希表记录每个字符的等待状态,hash[4](或 hash[0])记录空闲/新增青蛙数。
解法一:硬编码下标法(常规解法)
核心逻辑:
直接定义长度为 5 的数组,通过多个 else if 手动判断当前字符并转移状态。
代码框架:
// 1. 长度校验
if (c.length() % 5 != 0) return -1;
int[] hash = new int[5]; // 0:c, 1:r, 2:o, 3:a, 4:空闲
for (char ch : c.toCharArray()) {
if (ch == 'c') {
if (hash[4] != 0) hash[4]--;
hash[0]++;
} else if (ch == 'r' && hash[0] != 0) {
hash[0]--; hash[1]++;
}
// ... 依次 else if 处理 'o', 'a', 'k'
else return -1; // 乱序或非法字符拦截
}
// 检查是否有未叫完的青蛙
for (int i = 0; i < 4; i++) if (hash[i] != 0) return -1;
return hash[4];
特点:
- 优点:直观易懂,无需额外数据结构,空间复杂度极低。
- 缺点:代码冗长,如果蛙鸣单词变成更长的字符串(如10个字母),扩展性差。
解法二:HashMap + 动态索引法(通用/老师解法)
核心逻辑:
利用 HashMap 存储字符与下标的映射关系,通过 index.get(ch) 动态获取当前字符对应的数组下标,实现通用化状态转移。
代码框架:
// 1. 长度校验
if (c.length() % 5 != 0) return -1;
String t = "croak";
int n = t.length();
int[] hash = new int[n]; // 0:c, 1:r, 2:o, 3:a, 4:空闲
Map<Character, Integer> index = new HashMap<>();
// 初始化字符映射
for (int i = 0; i < n; i++) index.put(t.charAt(i), i);
for (char ch : c.toCharArray()) {
if (ch == t.charAt(0)) { // 遇到 'c'
if (hash[n-1] != 0) hash[n-1]--;
hash[0]++;
} else {
int i = index.get(ch); // 动态获取下标
// 防御:非法字符或前序状态为0(乱序)
if (i == null || hash[i-1] == 0) return -1;
hash[i-1]--; hash[i]++;
}
}
// 检查前4个状态是否清空
for (int i = 0; i < n-1; i++) if (hash[i] != 0) return -1;
return hash[n-1];
特点:
- 优点:代码简洁优雅,通用性强(适用于任何长度的固定顺序单词混合问题)。
- 缺点 :引入了
HashMap,有额外的空间开销。
⚠️ 踩坑与调试记录
- 变量作用域冲突 :同一作用域内不能声明同名变量。之前报错
variable i is already defined是因为在外层for(int i=0...)内部又写了int i = index.get(ch)。解决:内层改用idx或不同变量名。 - 逻辑嵌套错误 :之前误将字符遍历和结尾检查嵌套在初始化循环里,导致第一次迭代就
return。解决:初始化、遍历、检查三步必须平级并列。 - 防御性校验缺失 :必须加长度校验(
%5)拦截非法输入;HashMap解法需判空(i == null)防止遇到非croak字符时报错。
💡 适用场景
- 硬编码法:适合字符集小、题目固定的快速刷题场景。
- 通用映射法:适合需要抽象逻辑、字符集较大或面试中展示代码扩展性的场景。