题解:洛谷 P2908 Word Power
【题目来源】
洛谷:P2908 [USACO08OPEN] Word Power S - 洛谷
【题目描述】
约翰想要计算他那 \(N(l \le N \le 1000)\) 只奶牛的名字的能量。每只奶牛的名字由不超过 \(1000\) 个字符构成,没有一个名字是空字符串。
约翰有一张“能量字符串表”,上面有 \(M(1 \le M \le 100)\) 个代表能量的字符串。每个字符串由不超过 \(30\) 个字符构成,同样不存在空字符串。一个奶牛的名字蕴含多少个能量字符串,这个名字就有多少能量。所谓“蕴含”,是指某个能量字符串的所有字符都在名字串中按顺序出现(不一定一个紧接着一个)。
所有的大写字母和小写字母都是等价的。比如,在贝茜的名字 Bessie 里,蕴含有 Be、si、EE、Es 等等字符串,但不蕴含 Ls 或 eB。请帮约翰计算他的奶牛的名字的能量。
【输入】
第一行两个正整数 \(N,M\)。
下面 \(N\) 行,每行一个字符串,代表一只奶牛的名字。
下面 \(M\) 行,每行一个字符串,代表一个能量字符串。
【输出】
对每个名字输出一行一个整数表示其能量值。
【输入样例】
5 3
Bessie
Jonathan
Montgomery
Alicia
Angola
se
nGo
Ont
【输出样例】
1
1
2
0
1
【核心思想】
-
问题分析:给定 \(N\) 个奶牛名字和 \(M\) 个能量字符串,判断每个名字蕴含多少个能量字符串。"蕴含"指能量字符串的所有字符在名字中按顺序出现(不必连续),且大小写不敏感。这是一个字符串子序列匹配问题,关键在于对每个名字-能量串对进行按顺序的字符查找。
-
算法选择:
- 统一转小写:预处理将所有名字和能量字符串转为小写,消除大小写差异
- 顺序查找匹配:对每个能量字符串的每个字符,在名字中从上次找到位置的下一个位置开始查找,验证是否为子序列
-
关键步骤:
- 读取输入:\(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\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \cdot M \cdot L_{\text{name}} \cdot L_{\text{power}})\),最坏情况下对每个字符都执行一次
find - 空间复杂度:\(O(N \cdot L_{\text{name}} + M \cdot L_{\text{power}})\),存储字符串
- 时间复杂度:\(O(N \cdot M \cdot L_{\text{name}} \cdot L_{\text{power}})\),最坏情况下对每个字符都执行一次
-
子序列匹配的核心思想:
- 顺序性约束:能量字符串的字符必须在名字中保持相对顺序,通过维护 \(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
浙公网安备 33010602011771号