华为机试 - 数大雁
题目描述
一群大雁往南飞,给定一个字符串记录地面上的游客听到的大雁叫声,请给出叫声最少由几只大雁发出。
具体的:
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();
}