命令行选项

预处理每个选项是否带参数,按字符串顺序模拟解析并记录最后一次合法参数。

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 必须出现在格式字符串中。
  • 带参数选项的参数可能以 - 开头,所以必须无条件消费下一个字符串,不能把它重新作为选项判断。
  • 重复选项只输出一次,带参数选项保存最后一次值;从 az 扫描输出即可保证字母升序。

代码

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;
}

复杂度

预处理格式为 O(format)O(|format|)。每条命令行只扫描一次,时间复杂度为 O(L)O(L),额外空间复杂度为 O(1)O(1)

总结

字符串解析题的关键是明确“消费了哪些 token”。一旦选项要求参数,后一个字符串的语法不再重要;只有不期待参数时,非法字符串才会终止扫描。