华为机试 - 数大雁

华为机试 - 数大雁

题目描述

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

具体的:

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

说明

两声连续完整叫声 quack + quack;可以由一只鸟发出

示例2

输入:

qaauucqcaa

输出:

-1

说明

不存在任何完整鸟叫声

示例3

输入:

quacqkuackquack

输出:

2

说明

-- quacqkuackquack
-- quac k
--     q uack
--           quack
-- 可知,第一声与第二声存在交集,那么必须是两只鸟发出;第三声可以是第一只发出,也可以是第二只发出
思路

1)考虑将字符串映射到时间线上进行再进行判定;具体的时间线可以就是数组下标 => char 数组

2)遍历数组分别进行 q u a c k 的计数并存储起来

叫声 = quacqkuackquack
计数 = 111121222233333

3)遍历数组并保存状态:遍历1时,记录1到列表,逐一遍历并计数更新列表对应的值;当遇到2时,说明是新的叫声,那么我们需要检查有没有已经叫声完整(计数=0)的鸟,这里是没有的,那么也将2加入列表;当遇到 5 个 1 时,表示这只鸟有完整的叫声,将计数赋值为0;继续遍历遇到3,查询列表值存在 1、2 都为可继续叫的状态(计数 = 0);遍历完成后,列表内的个数即为鸟的最少只数。

思路转换:

从上诉3)中得知,只要保存鸟叫声的状态即可解决此题,那么我们可以直接保存鸟叫声的位置来判断

遍历字符串:

1)当遍历到第一个字符 Q 时,表示新的鸟叫,此时状态保存列表为空,那么进行添加 列表[0] = Q

2)当遍历到第二个字符 U 时,表示不是新的鸟叫,那么查看列表中是否有叫到Q的鸟,有的话进行更新 列表[0] = U

3)逐步遍历到第五个字符 Q 时,表示新的鸟叫,查看列表是否有完成鸟叫的鸟 列表[i] = 空字符,案例中不存在,那么需要添加到列表中,表示此叫声必须是新的鸟发出的;

4)当遍历到第六个字符 K 时,查看列表,发现列表[0] = C,符合此鸟发出的后续声音,那么更新为 列表[0] = K;但是此时此鸟已经完成一次完整的叫声,更新为 列表[0] = 空字符;表示此鸟可以继续发出叫声。

5)直到遍历到第三个 Q 时,查看列表,发现 列表[0] = 空串,可以发出声音,那么更新 列表[0] = Q;此案例中列表 0、1 都是空串,咱们选任意一只都可,这里选第一只;可以直接退出列表检查循环。

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    char[] talk = in.nextLine().toCharArray();
    System.out.println(nums(talk));
}

private static int nums(char[] talks) {
    String quack = "quack";
    // 用于记录鸟叫声的进度,q u a c k ' '
    List<Character> memory = new ArrayList<>(); 
    // 用于计算结果,memory的size 表示最少几只鸟;但是考虑可能有不合法的鸟叫
    // 比如有只鸟只叫了 q 后续部分不存在,那么此鸟不进行计数
    // 所以额外存储结构存储 memory 中叫声完整的鸟
    HashSet<Integer> hashSet = new HashSet<>();
    // 开始遍历
    for (char talk : talks) {
        int index = quack.indexOf(talk);
        // 检查字符是否为叫声的组成部分,如果不是的话,则不符合规范,可以跳过检查
        if(index == -1) { 
            continue;
        }
        // 如果还没保存鸟叫位置,即集合为空,且当前字符为Q开始字符,那么可以直接添加
        if(memory.isEmpty() && talk == quack.charAt(0)) {
            memory.add(talk);
        }
        else if (!memory.isEmpty()) { // 现在处理不为空的情况
            // 如果当前为Q,表示新的叫声,咱们看看鸟叫状态中,是否有已经叫完的鸟,如果有,可以让这个鸟叫
            // 如果当前不为Q,表示需要寻找一个状态,是当前叫声的前一个字符,比如K,需要寻找处于C的鸟
            boolean isContains = false; // 标识一下是否在鸟叫状态中找到对应的状态
            // 这里先看看我们需要查找什么状态的鸟;为 Q 时,需要找 空字符 的鸟
            // 不为 Q 时,需要找 当前字符的前一个叫声字符,如 K 需要找 C
            char aim = talk == quack.charAt(0) ? ' ' : quack.charAt(index - 1);
            // 遍历鸟
            for (int i = 0; i < memory.size(); i++) {
                char currentL = memory.get(i);
                if(currentL == aim) { // 找到了目标鸟
                    // 如果当前状态为K,表示完整的叫声
                    if(talk == quack.charAt(quack.length() - 1)) { 
                        memory.set(i, ' ');
                        // 完整叫声说明是一只可以统计的鸟,但是这只鸟可能后面还会叫
                        // 所以用 hashset 存储
                        hashSet.add(i); 
                    }
                    else {
                        memory.set(i, talk);
                    }
                    isContains = true;
                    break;
                }
            }
            // 如果状态中没找到,那么要么不合法,要么就是Q开头的新叫声,
            // 且不能是已经出现的鸟叫的,所以是新鸟
            if(!isContains && talk == quack.charAt(0)) {
                memory.add(quack.charAt(0));
            }
        }
        // 鸟叫状态为空且当前字符不为Q,表示不合法;不处理,直接跳过
    }

    return hashSet.size();
}
### 华为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]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值