首页 > 其他分享 >CCF/CSP认证-第一次-命令行选项

CCF/CSP认证-第一次-命令行选项

时间:2024-05-23 15:30:22浏览次数:19  
标签:选项 命令行 int ++ 参数 str 字符串 CCF CSP

1.问题

1.1 命令行选项

请你写一个命令行分析程序,用以分析给定的命令行里包含哪些选项。每个命令行由若干个字符串组成,它们之间恰好由一个空格分隔。这些字符串中的第一个为该命令行工具的名字,由小写字母组成,你的程序不用对它进行处理。在工具名字之后可能会包含若干选项,然后可能会包含一 些不是选项的参数。

选项有两类:带参数的选项和不带参数的选项。一个合法的无参数选项的形式是一个减号后面跟单个小写字母,如"-a" 或"-b"。而带参数选项则由两个由空格分隔的字符串构成,前者的格式要求与无参数选项相同,后者则是该选项的参数,是由小写字母,数字和减号组成的非空字符串。

该命令行工具的作者提供给你一个格式字符串以指定他的命令行工具需要接受哪些选项。这个字符串由若干小写字母和冒号组成,其中的每个小写字母表示一个该程序接受的选项。如果该小写字母后面紧跟了一个冒号,它就表示一个带参数的选项,否则则为不带参数的选项。例如, "abⓂ️" 表示该程序接受三种选项,即"-a"(不带参数),"-b"(带参数), 以及"-m"(带参数)。

命令行工具的作者准备了若干条命令行用以测试你的程序。对于每个命令行,你的工具应当一直向后分析。当你的工具遇到某个字符串既不是合法的选项,又不是某个合法选项的参数时,分析就停止。命令行剩余的未分析部分不构成该命令的选项,因此你的程序应当忽略它们。

输入格式

输入的第一行是一个格式字符串,它至少包含一个字符,且长度不超过 52。格式字符串只包含小写字母和冒号,保证每个小写字母至多出现一次,不会有两个相邻的冒号,也不会以冒号开头。

输入的第二行是一个正整数 N(1 ≤ N ≤ 20),表示你需要处理的命令行的个数。

接下来有 N 行,每行是一个待处理的命令行,它包括不超过 256 个字符。该命令行一定是若干个由单个空格分隔的字符串构成,每个字符串里只包含小写字母,数字和减号。

输出格式

输出有 N 行。其中第 i 行以"Case i:" 开始,然后应当有恰好一个空格,然后应当按照字母升序输出该命令行中用到的所有选项的名称,对于带参数的选项,在输出它的名称之后还要输出它的参数。如果一个选项在命令行中出现了多次,只输出一次。如果一个带参数的选项在命令行中出 现了多次,只输出最后一次出现时所带的参数。

样例输入

albw:x
4
ls -a -l -a documents -b
ls
ls -w 10 -x -w 15
ls -a -b -c -d -e -l

样例输出

Case 1: -a -l
Case 2:
Case 3: -w 15 -x
Case 4: -a -b

2.题解

2.1 字符串 + 哈希表 + 数组的综合应用(90分,有问题尚未解决,但不知道实际测试用例!!! 去除参数是否为命令的判断后拿到100分了)

思路

1.首先我们处理格式字符串,用一张哈希表记录有哪些命令和哪些命令需要参数
2.然后我们开始读取命令字符串,并且开始分解该字符串.
这里有几个注意点:
2.1 我们使用cin读取时,它自动以空格或者换行符为分隔符读取一个字符串,但这里很显然我们需要读取一整行的字符串,包含了空格。
所以我们采用getline来读取这一行字符串,但是这里有个问题,上面使用cin读取cnt个数时,是不读取末尾的换行符的,所以这里必须单独处理一下换行符,使用cin.ignore()或者getchar()即可

2.2 C++中并没有Java中类似split的拆分方法,但是

  • 1.如果我们知道字符串的格式(固定),可以直接使用sccanf进行读取
  • 2.如果不知道,就可以使用stringstream或者isstringstream流读取,配合while循环将字符拆出来

2.3 处理字符串时,略去第一个ls,我们开始进行拆解

  • 1.如果表达式成立, 那么第一个一定是一个命令而不是一个参数, 之后每次我们要么单独处理一个命令,要么处理一个命令以及它的参数。所以每次开头的还是一个命令才是合法的(解释第一个if); 然后我们开始处理有参数和无参数情况(这里参数似乎只要是减号,小写字符和数字组成即可,虽然这就有可能组成一个命令(减号配字母),但这里好像如果是重复命令也判断为参数);对于不合法的情况直接break跳出,然后输出即可。
  • 2.为了处理带参数的命令,只保留最后一次出现的参数,我们这里使用一个哈希表存储 命令 以及 其对应的参数(不用数组是因为我无法快速知道这个命令之前是否出现过,还得遍历)
  • 3.对于使用哈希表,其自身无序性会破坏原有的输入顺序,我们在之后用一个vector数组接收哈希表中的数据,并进行升序排序(发现这里的命令输入,其实是按字节序增大的方向输入的)

2.4 这里使用正则表达式判断是否为合法参数的方式也可以学习一下
^表示开头,$表示结尾
[]表示可以包含这些元素, +代表至少有一个(非空)
如果^在[]内,则表示不包括的意思

代码

#include <bits/stdc++.h>
using namespace std;

// 检测是否为合法参数 
bool isValidParameter(const string& str) {
    // 使用正则表达式检查字符串是否只包含小写字母、数字和减号,并且非空
    regex pattern("^[a-z0-9-]+$");
    return regex_match(str, pattern);
}

int main() {
    string op;
    unordered_map<char, int> mp;
    cin >> op;

    // 初始化格式字符串
    for (int i = 0; i < op.length(); i++) {
    	if(op[i] == ':') continue; 
        if (i + 1 < op.length() && op[i + 1] == ':') {
            mp[op[i]] = 1; // 带参数选项
            i++;
        } else {
            mp[op[i]] = 0; // 不带参数选项
        }
    }

    int cnt;
    cin >> cnt;
    cin.ignore(); // 忽略换行符, 为下面的getline做准备 

    for (int i = 0; i < cnt; i++) {
        // 1. 拆分字符串
        string input;
        getline(cin, input);
        istringstream iss(input);
        string temp;
        vector<string> words;
        while (iss >> temp) {
            words.push_back(temp);
        }

        printf("Case %d: ", i + 1);
        unordered_map<char, string> ans;
        bool error = false;

        // 2. 开始处理字符串
        for (int j = 1; j < words.size(); j++) {
            string word = words[j];
            // 当前字符串是命令 且 在格式字符串中出现(只要抓住第一个肯定是命令,后面如果遇到参数就有j++,就又到了判断命令,所以不用单独列一个判断参数的) 
            if (word[0] == '-' && word.length() == 2 && mp.count(word[1])) {
                char option = word[1];
                // 带参数选项
                if (mp[option] == 1) {
                	// 判断是否还有可能有参数且参数是否合法 
                    if (j + 1 < words.size() && isValidParameter(words[j + 1]) ) {
                        ans[option] = words[j + 1];
                        j++; // 跳过参数
                    } else {
                        break; // 非法参数 / 无后续字符串 
                    }
                } else { // 不带参数选项
                    ans[option] = "";
                }
            } else {
                break; // 未出现的命令 / 非法选项(参数开头/连续参数),停止解析
            }
        }


        // 按字母升序输出结果
        vector<pair<char, string>> sortedAns(ans.begin(), ans.end());
        sort(sortedAns.begin(), sortedAns.end());
        for (auto &p : sortedAns) {
            if (p.second.empty()) {
                cout << "-" << p.first << " ";
            } else {
                cout << "-" << p.first << " " << p.second << " ";
            }
        }
        cout << endl;
    }

    return 0;
}

2.2 方法一优化-既定数组

思路

这里相比上面的思路,是我们在得知了他输入的顺序是按字节序增大方向的输入顺序之后写出的,我们发现既然顺序提前已知(用哈希表保存不了输入顺序,用未定数组(push_back)无法处理参数的最后一个参数);
这里就使用已定数组,一共26个小写字母,使用一个vector,前面步骤一样,记录该字母是有参还是无参,后面再进行判断的时候可以进行新增或者参数更新即可!
由于顺序已定,我们在输出的时候,直接按字节序顺序进行遍历,如果该位存在,那么便输出,否则继续遍历

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 30;

bool op1[N], op2[N]; //op1存储无参数选项,op2存储有参数选项
int n;
string ans[N];

int main()
{
    string str;
    cin >> str; //读入所有的参数

    for (int i = 0; i < str.size(); i ++) //解析参数是否带参数
    {
        if (i + 1 < str.size() && str[i + 1] == ':') //如果下一位没有越界,并且下一位是冒号,则该位为带参选项
        {
            op2[str[i] - 'a'] = true; //表明str[i]为带参选项
            i ++; //跳过冒号
        }
        else
        {
            op1[str[i] - 'a'] = true;//表明str[i]为不带参选项
        }
    }

    cin >> n;
    getchar();//过滤回车

    for (int C = 1; C <= n; C ++)
    {
        printf("Case %d:", C);
        getline(cin, str); //读入整行需要用到getline,使用getline(如果上一个读入使用的是cin)需要将上一行的回车进行过滤
        string t;
        stringstream ssin(str);
        vector <string> ops;
        while(ssin >> t) ops.push_back(t);//将str按空格分层存储在数组

        for (int i = 0; i < 26; i ++) ans[i].clear();//将ans清空

        for (int i = 1; i < ops.size(); i ++) //第一个是名字,略过
        {
            if (ops[i][0] != '-' || ops[i][1] < 'a' || ops[i].size() != 2) break;
            int k = ops[i][1] - 'a';
            if(op1[k])
            {
                ans[k] = '*';
            }
            else if(op2[k] && i + 1 < ops.size())
            {
                ans[k] = ops[i + 1];
                i ++;
            }
            else break;
        }

        for (int i = 0; i < 26; i ++)
        {
            if(ans[i].size())
            {
                cout << " -" << char('a' + i);
                if(op2[i])
                {
                    cout << ' ' << ans[i];
                }
            }
        }
        cout << endl;
    }
    return 0;
}


2.3 双指针处理字符串

思路

这里提供一种不适用stringstream或者isstringstream处理字符串分隔符的思路,使用快慢双指针将字符串分隔

代码

#include <iostream>
#include <unordered_map>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
unordered_map <string, int> m; // <-a, 1/2>
vector <string> work(string str) //利用双指针将串根据空格进行分割
{
    vector <string> res;
    res.clear();

    for (int i = 0; i < str.size(); i ++)
    {
        int j = i; //i是字符
        string t = "";
        while (j < str.size() && str[j] != ' ') j ++; //j = str.size() 或 str[j] = ' '
        t = str.substr(i, j - i); 
        res.push_back(t);
        //cout << t << " ";
        i = j;
    }
    return res;
}

int main()
{
    string str;
    cin >> str;

    for (int i = 0; i < str.size(); i ++) //将格式串是否带参存储起来
    {
        string t = "-";
        t += str[i];
        if (str[i] == ':') continue;
        if ((i < str.size() - 1 && str[i + 1] != ':') || i == str.size() - 1)  m[t] = 1;
        else m[t] = 2;
        //cout << str[i] << " " << m[str[i]] << endl;
    }

    int n;
    cin >> n;
    getchar(); //读取回车
    
    // 处理字符串
    for (int i = 1; i <= n; i ++)
    {
        str = "";
        getline(cin, str); //读取整一行字符串

        vector <string> txt;
        txt.clear();
        txt = work(str);//将这行串根据空格进行拆分

        unordered_map <string, string> res; //<选项,参数》
        res.clear();

        for (int j = 1; j < txt.size(); j ++)
        {
            string x = txt[j];
            if (m[x] == 1)
            {
                res[x] = "*";
            }
            else if (m[x] == 2 && j < txt.size() - 1)
            {
                res[x] = txt[++j];
            }
            else break;
        }

        unordered_map <string, int> f;
        f.clear();
        // 按字节序进行排序
        sort(txt.begin(), txt.end());
        printf("Case %d:", i);
        for (auto x : txt)
        {
            if (res[x] != "" && f[x] == 0)
            {
                f[x] = 1;
                cout << " " << x;
                if (res[x] != "*") cout << " " << res[x];
            }
        }
        cout << endl;
    }
    return 0;
}

标签:选项,命令行,int,++,参数,str,字符串,CCF,CSP
From: https://www.cnblogs.com/trmbh12/p/18207608

相关文章

  • CSP历年复赛题-P1046 [NOIP2005 普及组] 陶陶摘苹果
    原题链接:https://www.luogu.com.cn/problem/P1046题意解读:30+伸手的高度,能够得着几个苹果。解题思路:直接模拟。100分代码:#include<bits/stdc++.h>usingnamespacestd;inta[15],h,ans;intmain(){for(inti=1;i<=10;i++)cin>>a[i];cin>>h;......
  • CSP历年复赛题-P1087 [NOIP2004 普及组] FBI 树
    原题链接:https://www.luogu.com.cn/problem/P1087题意解读:字符串作为根,左边一半作为左子树,右边一半作为右子树,递归构造数,并按FBI规则输出后续遍历结果。解题思路:按照题意,通过dfs来构造树,对于字符串str,提取左边一半递归构造左子树,提取右边一半递归构造右子树,前提是字符串长度>1......
  • CSP历年复赛题-P1085 [NOIP2004 普及组] 不高兴的津津
    原题链接:https://www.luogu.com.cn/problem/P1085题意解读:找到两数之和大于8且两数之和最大值的位置解题思路:不多说,送分题,直接模拟法即可100分代码:#include<bits/stdc++.h>usingnamespacestd;inta,b;intmaxx,maxn;intmain(){for(inti=1;i<=7;i++)......
  • Windows中实现将bat或exe文件作为服务_且实现命令行安装、配置、启动、删除服务
    一、背景描述在Windows环境下进行日常的项目开发过程中,有时候需要将bat文件或exe文件程序注册为Windows的服务实现开机自己运行(没有用户登陆,服务在开机后也可以照常运行)、且对于那些没有用户交互界面的exe程序来说只要在后台运行即可,无需在桌面显示该exe程序窗口内容、无......
  • CSP历年复赛题-P1044 [NOIP2003 普及组] 栈
    原题链接:https://www.luogu.com.cn/problem/P1044题意解读:一组数入栈、出栈的方案数,如果了解卡特兰数,此题可以秒杀;如果不了解,也可以通过递归或者递推来解决;最次,可以通过DFS暴搜出方案数,当然对于n个数,一共有n次入栈、n次出栈,一共2n次,每次要么入栈要么出栈,总搜索次数在22n规模,n最......
  • CSP历年复赛题-P1045 [NOIP2003 普及组] 麦森数
    原题链接:https://www.luogu.com.cn/problem/P1045题意解读:要计算2p-1的位数和最后500位,实际上只需要计算2p,两者位数一致,前者比后者个位减1即可,且个位肯定不会是0,比较容易处理。解题思路:如果直接采用高精度乘法计算2p,p最大3.1*106,高精度所用数组最长大概9*105,一共最多计算3.......
  • CSP历年复赛题-P1043 [NOIP2003 普及组] 数字游戏
    原题链接:https://www.luogu.com.cn/problem/P1043题意解读:将n个环形数分成任意m组,组内求和再%10、负数转正,组间相乘,求所有分组方案中得到结果的最小值和最大值。解题思路:比赛题的首要目的是上分!此题一看就是DP,但是苦苦思索了半天,想不清楚状态表示,那么可以换换策略,先暴力得分再......
  • 银河麒麟命令行修改密码
    背景:登录时不需要输入密码,可以进入桌面。为了安全,需要锁屏,锁屏需要输入密码。又不知道密码。另注:已经锁屏,重启。没有锁屏,直接桌面打开终端oem@oem-PC:~/桌面$passwdoem#知道密码的情况下先输入旧密码后输入新密码为oem更改密码当前密码:passwd:密码输入错误,......
  • CSP历年复赛题-P1037 [NOIP2002 普及组] 产生数
    原题链接:https://www.luogu.com.cn/problem/P1037题意解读:一个长整数,有若干数字替换规则,计算可以转换成多少种不同的整数。解题思路:看题之后,第一感觉,是用DFS:1、用字符串存储整数2、用领接表存储数字替换规则,因为一个数字可以替换成多个其他数字3、在dfs中,枚举字符串每个数字......
  • CSP历年复赛题-P1002 [NOIP2002 普及组] 过河卒
    原题链接:https://www.luogu.com.cn/problem/P1002题意解读:从A(0,0)点走到B(n,m)点,只能向右或者向下,C点以及其控制点不能走。解题思路:根据题意,此题要么递归(DFS),要么递推(动态规划)先分析数据规模,最大从起点到终点要走40步,每个步有2种走法,一共240种路径,DFS会超时,且方案数必须用longlong......