题解:洛谷 P2908 Word Power

【题目来源】

洛谷:P2908 [USACO08OPEN] Word Power S - 洛谷

【题目描述】

约翰想要计算他那 \(N(l \le N \le 1000)\) 只奶牛的名字的能量。每只奶牛的名字由不超过 \(1000\) 个字符构成,没有一个名字是空字符串。

约翰有一张“能量字符串表”,上面有 \(M(1 \le M \le 100)\) 个代表能量的字符串。每个字符串由不超过 \(30\) 个字符构成,同样不存在空字符串。一个奶牛的名字蕴含多少个能量字符串,这个名字就有多少能量。所谓“蕴含”,是指某个能量字符串的所有字符都在名字串中按顺序出现(不一定一个紧接着一个)。

所有的大写字母和小写字母都是等价的。比如,在贝茜的名字 Bessie 里,蕴含有 BesiEEEs 等等字符串,但不蕴含 LseB。请帮约翰计算他的奶牛的名字的能量。

【输入】

第一行两个正整数 \(N,M\)

下面 \(N\) 行,每行一个字符串,代表一只奶牛的名字。

下面 \(M\) 行,每行一个字符串,代表一个能量字符串。

【输出】

对每个名字输出一行一个整数表示其能量值。

【输入样例】

5 3
Bessie
Jonathan
Montgomery
Alicia
Angola
se
nGo
Ont

【输出样例】

1
1
2
0
1

【核心思想】

  1. 问题分析:给定 \(N\) 个奶牛名字和 \(M\) 个能量字符串,判断每个名字蕴含多少个能量字符串。"蕴含"指能量字符串的所有字符在名字中按顺序出现(不必连续),且大小写不敏感。这是一个字符串子序列匹配问题,关键在于对每个名字-能量串对进行按顺序的字符查找。

  2. 算法选择

    • 统一转小写:预处理将所有名字和能量字符串转为小写,消除大小写差异
    • 顺序查找匹配:对每个能量字符串的每个字符,在名字中从上次找到位置的下一个位置开始查找,验证是否为子序列
  3. 关键步骤

    • 读取输入\(N, M\)\(N\) 个名字,\(M\) 个能量字符串
    • 预处理:将所有字符串转为小写(tolower
    • 逐个判断(对每个名字 \(name[i]\)):
      • 初始化 \(cnt = 0\)
      • 遍历每个能量字符串 \(power[j]\)
        • \(pos = 0\)\(mark = 0\)
        • 遍历 \(power[j]\) 的每个字符 \(c\)
          • \(name[i]\) 中从 \(pos\) 开始查找 \(c\)
          • 若找到:\(pos = \text{找到位置} + 1\)
          • 若未找到:\(mark = 1\),跳出
        • \(mark = 0\)\(cnt \mathrel{+}= 1\)
      • 输出 \(cnt\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \cdot M \cdot L_{\text{name}} \cdot L_{\text{power}})\),最坏情况下对每个字符都执行一次 find
    • 空间复杂度:\(O(N \cdot L_{\text{name}} + M \cdot L_{\text{power}})\),存储字符串
  5. 子序列匹配的核心思想

    • 顺序性约束:能量字符串的字符必须在名字中保持相对顺序,通过维护 \(pos\) 指针确保每次查找在上次位置之后
    • 大小写无关:统一转小写后比较,避免复杂的分支判断
    • 线性扫描string::find 从指定位置开始线性扫描,最坏 \(O(L_{\text{name}})\),但由于 \(L_{\text{name}} \le 1000\)\(L_{\text{power}} \le 30\),实际效率可接受
    • 适用于"子序列判定 + 大小写不敏感"类问题,核心是顺序查找和位置维护

【解题思路】

【算法标签】

普及- #字符串入门

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, m;
string name[1005];
string power[105];
int main()
{
    cin >> n >> m;  // 输入n和m
    for (int i=1; i<=n; i++) {  // 输入n个name,需要将其统一转为小写字符串
        cin >> name[i];
        for (int j=0; j<name[i].length(); j++) {
            name[i][j] = tolower(name[i][j]);
        }
    }
    for (int i=1; i<=m; i++) {  // 输入m个能量字符串,也需要将其转为小写字符串
        cin >> power[i];
        for (int j=0; j<power[i].length(); j++) {
            power[i][j] = tolower(power[i][j]);
        }
    }
    for (int i=1; i<=n; i++) {  // 遍历n个name字符串
        int cnt = 0;  // 统计蕴含多少能量字符串,初始化为0
        for (int j=1; j<=m; j++) {  // 遍历m个能量字符串
            int mark = 0;  // 定义标记位
            int pos = 0;  // 定义字符串的查找起始位置,初始为0
            for (int k=0; k<power[j].length(); k++) {  // 遍历每个能量字符串的所有字符
                if (name[i].find(power[j][k], pos)>=0 && name[i].find(power[j][k], pos)<name[i].length()) {  // 如果在name[i]的长度范围内能找到
                    pos = name[i].find(power[j][k], pos)+1;  // 更新pos为找到位置的下一个位置
                } else {
                    mark=1;  // 否则修改mark
                    break;  // 并退出循环
                }
            }
            if (mark==0) cnt++;  // 如果k次循环结束后mark仍为0,说明所有字符都可以找到,cnt自增1
        }
        cout << cnt << endl;  // 输出结果
    }
    return 0;
}

【运行结果】

5 3
Bessie
Jonathan
Montgomery
Alicia
Angola
se
nGo
Ont
1
1
2
0
1
posted @ 2026-08-28 15:55  团爸讲算法  阅读(4)  评论(0)    收藏  举报