预处理每个选项是否带参数,按字符串顺序模拟解析并记录最后一次合法参数。
OJ: shumeng
题目 ID: CSP201403C
难度:普及-
标签:模拟字符串解析
日期: 2026-07-31 16:21
形式化题目
给定格式字符串(小写字母表示接受的选项,后面紧跟 : 表示带参数),以及若干条命令行。从工具名之后依次扫描,遇到既不是合法选项又不是合法选项参数的字符串就停止,忽略剩余部分。输出每条命令行用到的所有选项:按字母升序,带参数选项输出最后一次出现时带的参数。
思路
直接按格式字符串查找选项类型的写法如下:
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:51
*/
// brute.cpp:直接按格式字符串查找选项类型并模拟扫描。
#include <bits/stdc++.h>
using namespace std;
int option_position(const string &format, char option) {
for (int i = 0; i < (int)format.size(); i++) {
if (format[i] == option) {
return i;
}
}
return -1;
}
bool is_option_token(const string &token) {
return token.size() == 2 && token[0] == '-' && token[1] >= 'a' && token[1] <= 'z';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string format;
cin >> format;
int n;
cin >> n;
string line;
getline(cin, line);
for (int case_id = 1; case_id <= n; case_id++) {
getline(cin, line);
stringstream input(line);
string program_name, token;
input >> program_name;
bool used[26] = {0};
string value[26];
while (input >> token) {
if (!is_option_token(token)) {
break;
}
int position = option_position(format, token[1]);
if (position == -1) {
break;
}
int id = token[1] - 'a';
bool need_value = position + 1 < (int)format.size() && format[position + 1] == ':';
if (need_value) {
string parameter;
if (!(input >> parameter)) {
break;
}
used[id] = true;
value[id] = parameter;
} else {
used[id] = true;
}
}
cout << "Case " << case_id << ':';
for (int id = 0; id < 26; id++) {
if (!used[id]) {
continue;
}
cout << " -" << char('a' + id);
int position = option_position(format, char('a' + id));
if (position + 1 < (int)format.size() && format[position + 1] == ':') {
cout << ' ' << value[id];
}
}
cout << '\n';
}
return 0;
}正式程序把格式预处理为两个长度为 26 的数组:accepted[x] 表示 -x 是否允许,need_value[x] 表示它是否带参数。忽略工具名后从左到右扫描:当前字符串不是合法选项就停止;无参数选项直接标记;带参数选项读取下一个字符串并覆盖保存的参数。
解析规则
- 合法选项只允许形如
-x,且x必须出现在格式字符串中。 - 带参数选项的参数可能以
-开头,所以必须无条件消费下一个字符串,不能把它重新作为选项判断。 - 重复选项只输出一次,带参数选项保存最后一次值;从
a到z扫描输出即可保证字母升序。
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:51
*/
#include <bits/stdc++.h>
using namespace std;
bool accepted[26], need_value[26], used[26];
string value[26];
bool is_valid_option(const string &token) {
return token.size() == 2 && token[0] == '-' && token[1] >= 'a' && token[1] <= 'z' && accepted[token[1] - 'a'];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string format;
cin >> format;
for (int i = 0; i < (int)format.size(); i++) {
if (format[i] >= 'a' && format[i] <= 'z') {
int id = format[i] - 'a';
accepted[id] = true;
need_value[id] = i + 1 < (int)format.size() && format[i + 1] == ':';
}
}
int n;
cin >> n;
string line;
getline(cin, line);
for (int case_id = 1; case_id <= n; case_id++) {
getline(cin, line);
stringstream input(line);
string program_name;
input >> program_name;
string token;
while (input >> token) {
if (!is_valid_option(token)) {
break;
}
int id = token[1] - 'a';
if (need_value[id]) {
string parameter;
if (!(input >> parameter)) {
break;
}
used[id] = true;
value[id] = parameter;
} else {
used[id] = true;
}
}
cout << "Case " << case_id << ':';
for (int id = 0; id < 26; id++) {
if (!used[id]) {
continue;
}
cout << " -" << char('a' + id);
if (need_value[id]) {
cout << ' ' << value[id];
}
}
cout << '\n';
memset(used, 0, sizeof(used));
for (int id = 0; id < 26; id++) {
value[id].clear();
}
}
return 0;
}复杂度
预处理格式为
总结
字符串解析题的关键是明确“消费了哪些 token”。一旦选项要求参数,后一个字符串的语法不再重要;只有不期待参数时,非法字符串才会终止扫描。