USACO 2022 年 1 月铜组题目 "Cowphabet" 的 Python 题解
USACO 2022 年 1 月铜组题目 "Cowphabet" 的 Python 题解。
解题思路
核心问题
Bessie 按照 cowphabet 的顺序不断循环哼唱(如 a,b,c,...,z,a,b,c,...),Farmer John 只记住了他听到的部分字母序列。求最少哼唱了多少遍完整的 cowphabet。
关键观察
假设 cowphabet 顺序是 a,b,c,d,...:
- 如果听到的序列是
mood(正常字母顺序)m在第 1 遍出现o在m之后,仍在第 1 遍- 但
o后面是d,d在o之前!说明d必须在新的一遍中 - 所以
m,o在第 1 遍,o(第二个o)在第 2 遍... 不对,重新分析
正确分析
听到的字符串是按时间顺序的。如果当前字母在 cowphabet 中的位置 小于等于 上一个字母的位置,说明 Bessie 已经循环了一圈,必须开启新的一遍。
例如 cowphabet = abcdefghijklmnopqrstuvwxyz,听到 mood:
m(位置 12):第 1 遍开始o(位置 14):14 > 12,仍在第 1 遍o(位置 14):14 <= 14,必须新起一遍 → 第 2 遍d(位置 3):3 <= 14,必须新起一遍 → 第 3 遍
所以答案是 3。
算法步骤
- 建立 cowphabet 中每个字母到其位置的映射
- 遍历听到的字符串,比较当前字母和上一个字母的位置
- 如果当前位置 <= 上一个位置,遍数 +1
Python 代码
# 读取 cowphabet 顺序
cowphabet = input().strip()
# 读取 Farmer John 听到的字符串
heard = input().strip()
# 建立字母到位置的映射
pos = {}
for i, c in enumerate(cowphabet):
pos[c] = i
# 统计最少遍数
rounds = 1 # 至少需要 1 遍
prev_pos = pos[heard[0]] # 第一个字母的位置
for i in range(1, len(heard)):
curr_pos = pos[heard[i]]
# 如果当前字母在 cowphabet 中的位置 <= 上一个字母的位置
# 说明 Bessie 已经循环了一圈,需要新起一遍
if curr_pos <= prev_pos:
rounds += 1
prev_pos = curr_pos
print(rounds)
验证样例
输入:
abcdefghijklmnopqrstuvwxyz
mood
pos['m'] = 12,pos['o'] = 14,pos['d'] = 3
遍历:
m(12):rounds = 1, prev = 12o(14):14 > 12,不增加,prev = 14o(14):14 <= 14,rounds = 2, prev = 14d(3):3 <= 14,rounds = 3, prev = 3
输出:3 ✓
复杂度分析
| 项目 | 复杂度 | 说明 |
|---|---|---|
| 时间 | O(26 + n) = O(n) | n 为听到字符串长度,最大 1000 |
| 空间 | O(26) = O(1) | 固定大小的映射表 |
完整代码(带详细注释)
# USACO 2022 January Bronze - Cowphabet
# 题解:如果当前字母在 cowphabet 中的位置 <= 上一个字母的位置,
# 说明 Bessie 已经循环了一圈,需要新起一遍
# 读取 cowphabet 的 26 个字母顺序
cowphabet = input().strip()
# 读取 Farmer John 听到的字符串
heard = input().strip()
# 建立字母到位置的映射字典
# 例如正常字母表中:pos['a']=0, pos['b']=1, ..., pos['z']=25
pos = {}
for i, c in enumerate(cowphabet):
pos[c] = i
# 初始化:至少需要哼唱 1 遍
rounds = 1
# 记录上一个字母在 cowphabet 中的位置
prev_pos = pos[heard[0]]
# 从第二个字母开始遍历
for i in range(1, len(heard)):
curr_pos = pos[heard[i]]
# 关键判断:
# 如果当前字母在 cowphabet 中出现在上一个字母之前(或同一位置)
# 说明 Bessie 已经唱完一圈,必须重新开始唱
if curr_pos <= prev_pos:
rounds += 1
# 更新上一个位置
prev_pos = curr_pos
print(rounds)
为什么 <= 而不是 <?
考虑 heard = "aa":
- 第一个
a在第 1 遍 - 第二个
a也在第 1 遍?不对,Bessie 是按顺序循环唱的
实际上,Bessie 唱的是 a,b,c,...,z,a,b,c,...。如果第一个 a 在第 1 遍的开头,第二个 a 必须在第 2 遍的开头(因为第 1 遍已经唱过了 a)。
所以 curr_pos <= prev_pos 时,说明需要新起一遍。当相等时(如同一个字母连续出现),也需要新起一遍。
验证:heard = "aa",cowphabet = abc...
- 第一个
a(0):rounds = 1 - 第二个
a(0):0 <= 0,rounds = 2
答案是 2,正确。

浙公网安备 33010602011771号