题解:洛谷 P9940 Uddered but not Herd

【题目来源】

洛谷:P9940 [USACO21JAN] Uddered but not Herd B - 洛谷

【题目描述】

一个鲜为人知的事实是,奶牛拥有自己的文字:「牛文」。牛文由 \(26\) 个字母 az 组成,但是当奶牛说牛文时,可能与我们所熟悉的 abcdefghijklmnopqrstuvwxyz 不同,她会按某种特定的顺序排列字母。

为了打发时间,奶牛 Bessie 在反复哼唱牛文字母歌,而 Farmer John 好奇她唱了多少遍。

给定一个小写字母组成的字符串,为 Farmer John 听到 Bessie 唱的字母,计算 Bessie 至少唱了几遍完整的牛文字母歌,使得 Farmer John 能够听到给定的字符串。Farmer John 并不始终注意 Bessie 所唱的内容,所以他可能会漏听 Bessie 唱过的一些字母。给定的字符串仅包含他记得他所听到的字母。

【输入】

输入的第一行包含 \(26\) 个小写字母 az 的牛文字母表顺序。下一行包含一个小写字母组成的字符串,为 Farmer John 听到 Bessie 唱的字母。字符串的长度不小于 \(1\) 且不大于 \(1000\)

【输出】

输出 Bessie 所唱的完整的牛文字母歌的最小次数。

【输入样例】

abcdefghijklmnopqrstuvwxyz
mood

【输出样例】

3

【核心思想】

  1. 问题分析:给定一个牛文字母表顺序(26个字母的排列),以及Bessie哼唱时Farmer John听到的部分字母序列(可能漏听)。求Bessie至少完整哼唱了多少遍字母歌,才能让John听到给定的序列。由于每遍字母歌都是从字母表第一个唱到最后一个,John听到的序列必须能按顺序划分为若干段,每段都是字母表的一个子序列(不要求连续,因为可以漏听)。因此问题转化为:给定一个参照顺序(字母表),一个目标序列,求最少需要多少个“递增段”才能覆盖该序列,其中每一段内的字符在参照顺序中严格递增。

  2. 算法选择

    • 贪心扫描:遍历目标字符串,如果当前字符在前一个字符之后出现(在字母表顺序中位置更大),则可以放在同一遍中;否则,必须开始新的一遍。计数器加1。
    • 位置映射:用 pos[26] 数组记录每个字母在牛文字母表中的位置(0~25),以 O(1) 时间比较顺序。
  3. 关键步骤

    • 预处理:读入字母表顺序字符串 alpha(长度26)和目标字符串 s
    • 建立位置映射:遍历 alpha,令 pos[alpha[i] - 'a'] = i,记录每个字母在字母表中的排名。
    • 初始化:答案 ans = 1(至少需要唱一遍才能听到第一个字母)。
    • 扫描目标字符串:从 i = 1len(s)-1
      • pos[s[i]] <= pos[s[i-1]],说明当前字母在字母表中的位置不晚于前一个字母,无法在同一遍中按顺序唱到(因为同一遍中字母顺序必须严格递增),因此需要额外再唱一遍,ans++
      • 否则,说明可以接在上一遍的同一遍中,不需要增加遍数。
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(26 + |s|)\),主要开销在扫描目标字符串。
    • 空间复杂度:\(O(26)\),用于存储位置映射。
  5. 贪心模拟的核心思想

    • 分段本质:目标序列中,凡是在字母表顺序上出现“下降”或“持平”的位置,都必然被划分到不同的完整哼唱遍数中。因为每遍字母歌内字母顺序严格按字母表递增。
    • 只关心相邻字符:由于遍数划分只需满足每段内递增,那么只需检查相邻两个被听到的字符是否递增。如果递增,它们可归为同一遍;否则,必须开启新的一遍。这种贪心策略得到的遍数是最小的。
    • 适用场景:适用于判断一个序列能分解为多少个给定有序集合的子序列的问题,核心是比较相邻元素在参照顺序中的相对位置。

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
string s1, s2;
int ans=1;
int main()
{
    cin >> s1 >> s2;  // 输入字符串s1和s2
    for (int i=1; i<s2.length(); i++) {  // 遍历s2字符串
        if (s1.find(s2[i])<=s1.find(s2[i-1])) ans++;  // 查找s2[i]与s2[i-1]在s1中的先后位置,如果小于等于,则说明一遍中无法唱到,需要再唱一遍
    }
    cout << ans << endl;  // 最后打印结果
    return 0;
}

【运行结果】

abcdefghijklmnopqrstuvwxyz
mood
3
posted @ 2026-08-29 09:41  团爸讲算法  阅读(15)  评论(0)    收藏  举报