PAT甲级字符串相关题目题解(C/C++)


PAT甲级字符串相关题目题解

PAT1001(知识点:C++中to_string()的用法)

#include <iostream>
using namespace std;

int main()
{
    int x, y;
    cin >> x >> y;
    string sum_str = to_string(x + y);
    string res = "";
    for (int i = sum_str.size() - 1, j = 0; i >= 0; i--) // j用来存储当前已经遍历了几位
    {
        res = sum_str[i] + res;
        j++;
        if (j % 3 == 0 && i != 0 && sum_str[i - 1] != '-')
        {
            res = ',' + res;
        }
    }
    cout << res;
}

PAT1005(知识点:for-each遍历、字符ASCII码、数据长度估算)

知识点:for-each遍历、字符ASCII码、数据长度估算

  1. c - '0’可以得到字符对应的int值,如果直接输出c,输出的是这个数字字符的ASCII码值,易错点。

  2. 题目输入范围是 1 0 100 10^{100} 10100,而int的大小通常是32位,范围是 − 2 31 -2^{31} 231 2 31 − 1 2^{31}-1 2311,这里要估算一下 1 0 100 10^{100} 10100有没有超过int的范围,最后决定使用string存储输入值,而不是int(因为会越界)

python代码计算:

import math

# 计算2的31次方
power_of_two = 2 ** 31

# 转换为10的次方
power_of_ten = math.log10(power_of_two)

power_of_ten

要证明 (2^{31}) 约等于 (10^{9.33}) 的推理正确性,我们可以使用对数的换底公式来进行计算。换底公式是:

log ⁡ b a = log ⁡ c a log ⁡ c b \log_b a = \frac{\log_c a}{\log_c b} logba=logcblogca

其中,(a) 和 (b) 是大于0的实数,(c) 是正实数且 (c \neq 1)。我们想要计算的是 (2^{31}) 用 (10) 的次方来表示,即求 (10) 的多少次方等于 (2^{31})。

根据对数的定义,我们可以将 (2^{31}) 表示为 (10) 的对数:

1 0 x = 2 31 10^x = 2^{31} 10x=231

取对数得:

x = log ⁡ 10 ( 2 31 ) x = \log_{10} (2^{31}) x=log10(231)

使用换底公式,将底数从 (10) 改为 (2)(因为 (2) 和 (10) 的对数我们更容易理解和计算):

x = log ⁡ 2 ( 2 31 ) log ⁡ 2 ( 10 ) x = \frac{\log_2 (2^{31})}{\log_2 (10)} x=log2(10)log2(231)

由于 (\log_2 (2^{31}) = 31)(因为 (2) 的 (31) 次方等于 (2) 的 (31) 次方),我们可以简化 (x) 的表达式为:

x = 31 log ⁡ 2 ( 10 ) x = \frac{31}{\log_2 (10)} x=log2(10)31

我们知道 (\log_2 (10)) 大约等于 (3.32193),所以:

x ≈ 31 3.32193 x \approx \frac{31}{3.32193} x3.3219331

计算 (x) 的值:

x ≈ 9.33 x \approx 9.33 x9.33

因此,我们可以得出结论,(2^{31}) 约等于 (10^{9.33}),证明了推理的正确性。

#include <iostream>
using namespace std;
int main()
{
    string num;
    cin >> num;

    int current_num = 0;
    for (char c : num)
    {
        current_num += c - '0';
    }

    string eng_nums[] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
    string current_num_str = to_string(current_num);
    cout << eng_nums[current_num_str[0] - '0'];
    for (int i = 1; i < current_num_str.size(); i++)
    {
        cout << " " << eng_nums[current_num_str[i] - '0'];
    }
    return 0;
}

PAT1006(知识点:对于长度和格式相同的时间字符串,直接字典序比较(><)即可)

#include <iostream>
using namespace std;

int main()
{
    int m; // total number of records
    cin >> m;
    string id, in_time, out_time;
    string open_id, open_time, close_id, close_time;
    for (int i = 0; i < m; i++)
    {
        cin >> id >> in_time >> out_time;
        // find open time
        if (i == 0 || in_time < open_time)
        {
            open_time = in_time;
            open_id = id;
        }
        // find close time
        if (i == 0 || out_time > close_time)
        {
            close_time = out_time;
            close_id = id;
        }
    }
    cout << open_id << " " << close_id;
    return 0;
}

PAT1035

基础代码

复习puts()

#include <iostream>

using namespace std;

const int N = 1010;

string name[N], pwd[N];

string change(string str)
{
    string res;

    for (auto c : str)
        if (c == '1') res += '@';
        else if (c == '0') res += '%';
        else if (c == 'l') res += 'L';
        else if (c == 'O') res += 'o';
        else res += c;

    return res;
}

int main()
{
    int n;
    cin >> n;

    int m = 0;
    for (int i = 0; i < n; i ++ )
    {
        string cur_name, cur_pwd;
        cin >> cur_name >> cur_pwd;
        string changed_pwd = change(cur_pwd);

        if (changed_pwd != cur_pwd)
        {
            name[m] = cur_name;
            pwd[m] = changed_pwd;
            m ++ ;
        }
    }

    if (!m)
    {
        if (n == 1) puts("There is 1 account and no account is modified");
        else printf("There are %d accounts and no account is modified\n", n);
    }
    else
    {
        cout << m << endl;
        for (int i = 0; i < m; i ++ ) cout << name[i] << ' ' << pwd[i] << endl;
    }

    return 0;
}

使用STL

优化:
可以省略 make_pair 并直接使用 emplace_back 的原因在于 emplace_back 函数的工作方式。emplace_back 是C++11引入的一个方法,它直接在容器的末尾就地构造一个元素,而不是先构造一个临时对象然后复制或移动到容器中。这意味着你可以直接传递构造函数的参数给 emplace_back,它会使用这些参数来构造容器中的新元素。

对于 std::pair,其构造函数可以接受两个参数——分别对应于 pairfirstsecond 成员变量的值。因此,当你使用 emplace_back 向一个存储 std::pair 对象的 std::vector 中添加元素时,可以直接传递这两个参数给 emplace_back,而无需显式创建一个 std::pair 对象。这样做不仅简化了代码,还提高了效率,因为它避免了创建临时 pair 对象和随后的复制或移动操作。

在原始代码中:

records.emplace_back(make_pair(id, password));

使用 make_pair 创建了一个 pair 对象,然后传递给 emplace_back。而简化后的代码:

modifiedRecords.emplace_back(id, password);

直接将 idpassword 作为参数传递给 emplace_back,它会利用这两个参数就地构造一个 std::pair 对象,避免了额外的构造和复制或移动开销。

这种优化是基于 emplace_back 的设计理念,即直接在容器内部构造元素,减少不必要的临时对象的创建和销毁,从而提高代码的执行效率。

#include <iostream>
#include <vector>
using namespace std;

// 1 to @, 0 to %, l to L, O to o
bool change_password(string &password) // 注意这里要是引用类型,才能更改到传入的原始string
{
    bool changed_flag = false;
    for (char &c : password)
    {
        switch (c)
        {
        case '1':
            c = '@';
            changed_flag = true;
            break;
        case '0':
            c = '%';
            changed_flag = true;
            break;
        case 'l':
            c = 'L';
            changed_flag = true;
            break;
        case 'O':
            c = 'o';
            changed_flag = true;
            break;
        }
    }
    return changed_flag;
}
int main()
{

    int n;
    cin >> n;
    vector<pair<string, string>> records;

    int m = 0; // be modified account

    for (int i = 0; i < n; i++)
    {
        string id, password;
        cin >> id >> password;
        if (change_password(password)) // save changed accounts
        {
            records.emplace_back(id, password);
            m++;
        }
    }

    if (records.empty())
    {
        if (n == 1)
        {
            cout << "There is 1 account and no account is modified";
        }
        else
        {
            cout << "There are " << n << " accounts and no account is modified";
        }
    }
    else
    {
        cout << m;
        for (auto record : records)
        {
            cout << "\n"
                 << record.first << " " << record.second;
        }
    }

    return 0;
}

PAT1036

这一题自己做的时候有一个测试用例一直通过不了,这里暂存代码,以后寻找哪里有问题

#include <iostream>
using namespace std;

int main()
{
    // lowest grade of male highest grade of female
    int n;
    cin >> n;
    string low_m_name, high_f_name, low_m_id, high_f_id;
    int low_grade = 2000, high_grade = -100;
    bool exist_M, exist_F = false;
    for (int i = 0; i < n; i++)
    {
        string name, sex, id;
        int grade;
        cin >> name >> sex >> id >> grade;
        if (sex == "M" && grade < low_grade) // find lowest
        {
            exist_M = true;
            low_m_name = name;
            low_m_id = id;
            low_grade = grade;
        }
        else if (sex == "F" && grade > high_grade) // find highest
        {
            exist_F = true;
            high_f_name = name;
            high_f_id = id;
            high_grade = grade;
        }
    }

    if (!exist_F || !exist_M) // 缺失任意性别
    {
        cout << "Absent\n";
        if (exist_F)
        {
            cout << high_f_name << " " << high_f_id;
        }
        else
        {
            cout << low_m_name << " " << low_m_id;
        }
        cout << "\nNA";
    }
    else
    {
        cout << high_f_name << " " << high_f_id << "\n";
        cout << low_m_name << " " << low_m_id << "\n";
        cout << to_string(high_grade - low_grade);
    }
    return 0;
}

可以通过测试样例的答案,主要更正了输出部分,注意这部分的逻辑不能只用else不考虑各种可能的情况,一定要严谨,要与题目描述的流程和逻辑一致

#include <iostream>
using namespace std;

int main()
{
    // lowest grade of male highest grade of female
    int n;
    cin >> n;
    string low_m_name, high_f_name, low_m_id, high_f_id;
    int low_grade = 101, high_grade = -1;
    bool exist_M = false, exist_F = false;
    for (int i = 0; i < n; i++)
    {
        string name, sex, id;
        int grade;
        cin >> name >> sex >> id >> grade;
        if (sex == "M" && grade < low_grade) // find lowest
        {
            exist_M = true;
            low_m_name = name;
            low_m_id = id;
            low_grade = grade;
        }
        else if (sex == "F" && grade > high_grade) // find highest
        {
            exist_F = true;
            high_f_name = name;
            high_f_id = id;
            high_grade = grade;
        }
    }

    if (!exist_F) {
        cout << "Absent\n";
    } else {
        cout << high_f_name << " " << high_f_id << "\n";
    }
    if (!exist_M) {
        cout << "Absent\n";
    } else {
        cout << low_m_name << " " << low_m_id << "\n";
    }
    
    if (!exist_F || !exist_M) { // 如果缺少任意一种性别的学生
        cout << "NA";
    } else {
        cout << high_grade - low_grade;
    }
    return 0;
}

PAT1050(unordered_setcount()、遍历时不能删除)

#include <iostream>
#include <unordered_set>
using namespace std;

int main()
{
    string s1, s2;
    getline(cin, s1);
    getline(cin, s2);
    // 判断某元素是否在某集合中出现过用hash

    unordered_set<char> hash_set;
    for (char c : s2) // 将s2中的每个元素存入hash_set
    {
        hash_set.insert(c);
    }

    string res = "";
    for (char c : s1)
    {
        if (!hash_set.count(c)) // 遍历s1,没有在s2中出现过的元素添加到res中
        {
            res += c;
        }
    }
    cout << res;
    return 0;
}

unordered_set中方法count和find的区别

在C++中,std::unordered_set 是基于哈希表的集合容器,它提供了快速的元素查找、插入和删除操作。std::unordered_set 提供了 countfind 两个用于检查元素是否存在的成员函数,它们的主要区别在于它们的返回值和使用场景:

count 方法
  • 原型size_type count(const Key& key) const;
  • 功能:返回集合中等于 key 的元素的数量。对于 unordered_set,这个值只能是 01,因为 unordered_set 不允许重复的元素。
  • 返回值1 表示集合中存在该元素,0 表示不存在。
  • 使用场景:当你只需要知道某个元素是否存在于集合中时,可以使用 count。由于 unordered_set 中的元素唯一,count 方法实际上用来判断元素是否存在。
find 方法
  • 原型iterator find(const Key& key) const;
  • 功能:搜索集合中等于 key 的元素,并返回指向该元素的迭代器。如果找不到这样的元素,find 方法将返回一个指向集合末尾 (end()) 的迭代器。
  • 返回值:指向找到的元素的迭代器;如果未找到,则返回 end() 迭代器。
  • 使用场景:当你不仅需要知道某个元素是否存在,而且还可能需要访问该元素时,应该使用 find。通过检查 find 的返回值是否等于 end(),可以判断元素是否存在,并且可以直接通过迭代器操作找到的元素。

总结:

  • 使用 count 当你只需要判断元素是否存在,不需要访问元素本身。
  • 使用 find 当你需要判断元素是否存在,并且在存在的情况下需要直接访问该元素。

find 方法比 count 更灵活,因为它提供了对找到的元素的直接访问,而 count 更适合于简单的存在性检查。在性能方面,两者在 unordered_set 中通常都是非常快的,因为底层的哈希表使得查找操作的时间复杂度接近于常数时间。

字符串中删除某个元素

在C++中,如果你想删除字符串中某个特定位置的字符,可以使用std::string类的erase成员函数。erase函数有几个重载版本,但是为了删除特定位置的字符,你可以使用以下两种方式之一:

1. 删除指定位置的单个字符

你可以指定要删除的字符的位置(索引),使用erase函数的这个版本:

string& erase(size_t pos = 0, size_t len = npos);

这里,pos是要删除字符的位置(索引从0开始),len是要删除的字符数量。如果你只想删除一个字符,可以将len参数省略或设置为1。

示例代码:
#include <iostream>
#include <string>
using namespace std;

int main() {
    string str = "Hello, World!";
    // 删除位置为6的字符(即空格)
    str.erase(6, 1); // 注意:索引从0开始计数
    cout << str << endl; // 输出: Hello,World!
    return 0;
}

2. 删除由迭代器指定的单个字符

如果你更倾向于使用迭代器,erase还有一个重载版本,接受一个指向要删除字符的迭代器:

iterator erase(iterator position);
示例代码:
#include <iostream>
#include <string>
using namespace std;

int main() {
    string str = "Hello, World!";
    // 删除位置为6的字符(即空格),使用迭代器
    str.erase(str.begin() + 6);
    cout << str << endl; // 输出: Hello,World!
    return 0;
}

在这两个示例中,我们都删除了字符串"Hello, World!"中位置为6的字符(空格),结果是"Hello,World!"。选择哪种方式取决于你的具体需求和个人偏好。

不能在遍历字符串的时候删除元素

在C++中,如果想在遍历字符串 s1 的同时删除在 s2 中出现过的字符,不能直接在遍历过程中修改 s1,因为这可能会导致迭代器失效或跳过某些字符。相反,一种常见的做法是创建一个新的字符串,只包含那些未出现在 s2 中的字符。

PAT1071(lexicographically)

lexicographically:按字典排序地

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值