6.4 模拟专题:LeetCode1419.数青蛙
1.题目链接:数青蛙 - LeetCode
2.题目描述
给定一个字符串 croakOfFrogs,表示青蛙的鸣叫声序列。每个青蛙必须按顺序发出完整的 “croak” 字符,且多只青蛙可以同时鸣叫。要求计算最少需要多少只青蛙才能完成该字符串,若无法完成则返回 -1。
示例:
 输入:"croakcroak",输出:1(一只青蛙可重复鸣叫两次)。
3.示例分析
以输入 "crcoakroak" 为例,分析过程如下:
- 第一个 ‘c’ 开始,对应青蛙1。
- 遇到第二个 ‘c’ 时,没有已完成的青蛙(此时青蛙1未完成),需新增青蛙2。
- 最终青蛙1和青蛙2交替完成各自的 “croak”,最终需要2只青蛙。
4.算法思路
核心思想
通过状态哈希表跟踪每个字符的状态,确保字符顺序合法,并复用已完成鸣叫的青蛙。
具体步骤
- 状态定义:使用数组 hash记录每个字符(‘c’,‘r’,‘o’,‘a’,‘k’)的出现次数。
- 字符处理: 
  - 遇到 ‘c’ 时,优先复用已完成鸣叫的青蛙(hash[4] > 0)。
- 遇到其他字符时,必须存在前一个字符的状态(如 ‘r’ 前必须有 ‘c’)。
 
- 遇到 ‘c’ 时,优先复用已完成鸣叫的青蛙(
- 状态转移:每个字符处理时,减少前驱字符计数,增加当前字符计数。
- 结果验证:最终所有中间状态(‘c’,‘r’,‘o’,‘a’)必须清零,hash[4]的值即为最少青蛙数。
5.边界条件与注意事项
- 非法顺序:若字符出现时其前驱状态不存在(如直接出现 ‘r’ 而无 ‘c’),返回 -1。
- 未完成状态:遍历结束后若中间状态未清零,说明存在未完成的鸣叫,返回 -1。
- 首字符处理:遇到 ‘c’ 时若无可复用青蛙,需新增计数。
6.代码实现
class Solution {
public:
    int minNumberOfFrogs(string croakOfFrogs) {
        string base = "croak";
        int n = base.size();
        vector<int> hash(n, 0); // 记录各字符的状态数
        unordered_map<char, int> index; // 字符到索引的映射
        
        for (int i = 0; i < n; ++i) 
            index[base[i]] = i;
        
        for (char ch : croakOfFrogs) {
            int idx = index[ch];
            if (ch == 'c') {
                // 复用已完成鸣叫的青蛙
                if (hash[n-1] > 0) hash[n-1]--;
                hash[0]++;
            } else {
                // 检查前驱状态是否存在
                if (hash[idx-1] == 0) return -1;
                hash[idx-1]--;
                hash[idx]++;
            }
        }
        
        // 验证中间状态是否清零
        for (int i = 0; i < n-1; ++i) {
            if (hash[i] != 0) return -1;
        }
        return hash[n-1];
    }
};

7.总结
本算法通过维护字符状态哈希表,实现了对青蛙鸣叫过程的精准模拟,时间复杂度为 O(n),空间复杂度 O(1)。关键在于复用青蛙和严格的状态转移检查,确保算法的效率与正确性。
