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 遍出现
    • om 之后,仍在第 1 遍
    • o 后面是 ddo 之前!说明 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。

算法步骤

  1. 建立 cowphabet 中每个字母到其位置的映射
  2. 遍历听到的字符串,比较当前字母和上一个字母的位置
  3. 如果当前位置 <= 上一个位置,遍数 +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 = 12
  • o (14):14 > 12,不增加,prev = 14
  • o (14):14 <= 14,rounds = 2, prev = 14
  • d (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,正确。

posted @ 2026-06-28 07:16  kkman2000  阅读(5)  评论(0)    收藏  举报