【OD机试题解法笔记】数大雁

题目描述

一群大雁往南飞,给定一个字符串记录地面上的游客听到的大雁叫声,请给出叫声最少由几只大雁发出。

具体的:

​ 1.大雁发出的完整叫声为”quack“,因为有多只大雁同一时间嘎嘎作响,所以字符串中可能会混合多个”quack”。

​ 2.大雁会依次完整发出”quack”,即字符串中’q’ ,‘u’, ‘a’, ‘c’, ‘k’ 这5个字母按顺序完整存在才能计数为一只大雁。如果不完整或者没有按顺序则不予计数。

​ 3.如果字符串不是由’q’, ‘u’, ‘a’, ‘c’, ‘k’ 字符组合而成,或者没有找到一只大雁,请返回-1。

输入描述
一个字符串,包含大雁quack的叫声。1 <= 字符串长度 <= 1000,字符串中的字符只有’q’, ‘u’, ‘a’, ‘c’, ‘k’。

输出描述
大雁的数量

** 示例1
输入
quackquack

输出
1

说明>

** 示例2
输入:
qaauucqcaa

输出:
-1

说明>

** 示例3
输入:
quacqkuackquack

输出:
2

说明>

** 示例4
输入:
qququaauqccauqkkcauqqkcauuqkcaaukccakkck

输出:
5

说明>

** 示例5
输入:
quacqkuquacqkacuqkackuack

输出:
3

说明>

问题分析

题目要求计算最少需要几只大雁才能发出给定的混合叫声字符串。关键约束是:

  1. 每只大雁必须按顺序完整发出 “quack”(q→u→a→c→k)
  2. 多只大雁的叫声可以叠加,但单个大雁的叫声不能中断或逆序
  3. 若字符串包含无效字符、叫声不完整(如缺少某个字母)或顺序错误,返回 -1

思路: 状态计数 + 复用机制

  1. 状态计数:用5个变量(q/u/a/c/k)分别记录当前处于每个状态的大雁数量

    • 例如:q 表示已发出 ‘q’ 但未发出 ‘u’ 的大雁数量
    • u 表示已发出 ‘qu’ 但未发出 ‘a’ 的大雁数量,以此类推
  2. 顺序流转逻辑

    • 遇到 ‘q’:新增一只大雁进入 ‘q’ 状态;若有大雁已完成整个 “quack”(即 k>0),可复用这只大雁(k--),避免重复计数
    • 遇到 ‘u’:只能由处于 ‘q’ 状态的大雁流转而来(q--, u++
    • 遇到 ‘a’:只能由处于 ‘u’ 状态的大雁流转而来(u--, a++
    • 遇到 ‘c’:只能由处于 ‘a’ 状态的大雁流转而来(a--, c++
    • 遇到 ‘k’:只能由处于 ‘c’ 状态的大雁流转而来(c--, k++),此时大雁完成一次完整叫声
  3. 合法性校验

    • 最终需满足:除 k 外,其他状态(q/u/a/c)的数量必须为 0(所有大雁要么完成叫声,要么流程中断)
    • k=0(无完整叫声)或其他状态有剩余(流程中断),返回 -1
    • 否则 k 即为最少大雁数量(每只大雁可循环发出 “quack”)

关键细节

  • 复用机制:当再次遇到 ‘q’ 时,优先复用已完成叫声(处于 k 状态)的大雁,这样能最小化大雁数量(核心优化点)
  • 顺序校验:若某字符无法找到前序状态(如无 ‘q’ 却出现 ‘u’),会导致前序状态数量为负,但最终通过 “其他状态是否为0” 校验即可覆盖(例如示例2中,最终 q/u/a/c 均有剩余,返回 -1)

示例推导(示例3:quacqkuackquack)

输入字符串:q u a c q k u a c k q u a c k

  1. 流程流转:
    • q → u → a → c(此时 c=1)
    • 新 q(复用机制:无 k,q=1)→ k(前一个 c 流转为 k,k=1, c=0)
    • u(q 流转为 u,q=0, u=1)→ a → c → k(k=2, c=0)
    • q → u → a → c → k(k=3?不,实际复用 k=2 中的一只,最终 k=2)
  2. 最终状态:q=0, u=0, a=0, c=0, k=2 → 输出 2(符合示例3结果)
边界情况处理
  1. 字符串长度不是5的倍数:直接返回 -1(隐含在最终状态校验中,如长度为3,必然有状态剩余)
  2. 字符顺序错误(如 qaauucqcaa):最终 q/u/a 均有剩余,返回 -1
  3. 单只大雁多次鸣叫(如 quackquack):k=1,其他状态为0,返回1
代码逻辑梳理
  1. 初始化5个状态变量为0
  2. 遍历字符串,按字符类型更新对应状态(流转前序状态,复用后续状态)
  3. 遍历结束后,检查 q/u/a/c 是否均为0且 k>0:
    • 是 → 输出 k
    • 否 → 输出 -1

时间复杂度 O(n)(n为字符串长度),空间复杂度 O(1)(仅用5个变量),高效且符合题目约束。

代码

function solution() {
    const s = readline().split('');
    let q = 0;
    let u = 0;
    let a = 0;
    let c = 0;
    let k = 0;
    for (let ch of s) {
        if (ch === 'q') {
            q++;
            if (k) {
                k--;
            }
        } else if (ch === 'u') {
            u++;
            if (q) {
                q--;
            }            
        } else if (ch === 'a') {
            a++;
            if (u) {
                u--;
            }            
        } else if (ch === 'c') {
            c++;
            if (a) {
                a--;
            }
        } else if (ch === 'k') {
            k++;
            if (c) {
                c--;
            }
        } else {
            console.log(-1);
            break;
        }
    }

    console.log(!k ? -1 : k);
}




// 测试用例
const cases = [
    `quackquack`,
    `qaauucqcaa`,
    `quacqkuackquack`,
    `qququaauqccauqkkcauqqkcauuqkcaaukccakkck`,
    'quacqkuquacqkacuqkackuack'
];

let caseIndex = 0, lineIndex = 0;
const readline = (() => {
    let lines = [];
    return () => {
        if (!lineIndex) lines = cases[caseIndex].trim().split('\n').map(l => l.trim());
        return lines[lineIndex++];
    };
})();

cases.forEach((_, i) => {
    caseIndex = i;
    lineIndex = 0;
    solution();
    console.log('-------');
});

### 华为OD试 E卷 大雁 算法题 解答思路 #### 题目解析 该问题的核心在于通过给定的字符串判断至少有多少只大雁发出了完整的“quack”叫声。每只大雁发出的声音必须严格遵循“q-u-a-c-k”的顺序,且不允许跳过任何字母[^2]。 为了实现这一目标,可以采用一种计制来跟踪当前正在发声的大雁量以及它们所处的状态。具体来说,“q”表示一只新大雁开始发声;而每当完成一次“quack”,就减少一只大雁量并增加总的统计目。 #### 实现方法 以下是基于贪心算法的一种解决方案: 1. **状态定义**: 使用五个变量分别记录处于不同阶段(即‘q’, ‘u’, ‘a’, ‘c’, ‘k’)的大雁量。 2. **遍历输入串**: 对于每一个字符,依据其在“quack”序列中的位置调整对应状态变量的值。 3. **边界条件处理**: 如果发现某个字符无法匹配到合适的位置,则返回错误提示或者认为据非法。 4. **最终结果计算**: 当整个字符串被扫描完毕之后,检查是否有未完成叫唤循环的情况存在,并据此得出最小所需大雁。 下面提供了一个 Python 版本的具体代码示例: ```python def min_ducks(s): quacks = 'quack' n = len(quacks) count = [0]*n # 记录各个阶段的大雁量 total = 0 # 至少需要多少只大雁 current = 0 # 正在发声的大雁量 i = 0 # 初始化指针用于追踪'quack' for char in s: pos = quacks.find(char) if pos == -1: continue # 忽略无关字符 elif pos == 0: if current >= total: total += 1 current += 1 count[pos] += 1 else: prev_pos = (pos - 1) % n if count[prev_pos] > 0: count[prev_pos] -= 1 if pos != n-1: count[pos] += 1 else: current -= 1 else: return -1 # 不合法情况 for j in range(n-1): # 检查是否还有未结束的大雁 if count[j]>0: return -1 # 若有则不满足要求 return total # 返回最少大雁 if __name__ == "__main__": test_str = input().strip() result = min_ducks(test_str) print(result if result!=-1 else "Data Error!") ``` 此程序能够有效解决上述提到的问题,并且时间复杂度接近 O(N),其中 N 表示输入字符串长度,非常适合在此类竞赛环境中应用[^4]。 #### 注意事项 需要注意的是,在实际开发过程中还需要考虑更多异常状况比如空格或者其他干扰项的存在等问题,这里仅作为基础模型展示[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值