PAT甲级题目翻译+答案 AcWing(字符串处理)
1001 A+B Format (20 分)
- 题意 :将整数转换成标准格式
- 思路 :从后往前遍历字符串进行模拟,每三个数字加一个逗号,但不能是在最前面加逗号,也不能是加在负号后面
#include <iostream>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
string num = to_string(a + b);
string ans = "";
for (int i = num.size() - 1, j = 0; i >= 0; i -- )
{
ans = num[i] + ans;
++ j;
if (j % 3 == 0 && i && num[i - 1] != '-') ans = "," + ans;
}
cout << ans;
return 0;
}
1005 Spell It Right (20 分)
- 题意 :得到输入数的各位之和后按位输出分别的英文
- 语法 :行末无空格,可以先输出第一位后,分别输出一个空格加每一位
#include <iostream>
using namespace std;
string word[] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
int main()
{
string n;
cin >> n;
int s = 0;
for (auto c : n) s += c - '0';
string str = to_string(s);
cout << word[str[0] - '0'];
for (int i = 1; i < str.size(); i ++ ) cout << ' ' << word[str[i] - '0'];
return 0;
}
1006 Sign In and Sign Out (25 分)
- 题意 :在输入的每个第二个字符串中找最小的,第三个找最大的,输出它们分别对应的第一个字符串
#include <iostream>
using namespace std;
int main()
{
string open_id, open_time;
string close_id, close_time;
int m;
cin >> m;
for (int i = 0; i < m; i ++ )
{
string id, in_time, out_time;
cin >> id >> in_time >> out_time;
// 更新
if (!i || in_time < open_time)
{
open_id = id;
open_time = in_time;
}
if (!i || out_time > close_time)
{
close_id = id;
close_time = out_time;
}
}
cout << open_id << ' ' << close_id;
return 0;
}
1016 Phone Bills (25 分)
题意 :
- chronologically时间先后顺序;alphabetical order字典序
- 给出每个时间段内每分钟的话费,给出n条消息包含用户名称,月日时分,打开还是关闭状态
- 要求按字典序输出所有用户的账单,账单包含用户名称和发生的月份,再按时间先后顺序分别输出每一段话费的起始时间和时长和价格,最后输出总价格
- 保证所有日期在同一个月内,且每个on对应的off是输入中的时间顺序的下一个off,无视无法配对的
- 注意给出时间段的价格是美分,而最后输出是美元,除以100.0
思路 :
- 将所有时间都转换成这个月的分钟数,但输出时还要输出这种格式化的时间,所以原先格式化的时间也要存下来
- 注意到如果每条通话记录的话费按照每一分钟来算,时间复杂度非常高,500(人) * 31 * 1440 * 24,而且这道题时间限制是0.2秒。那么怎么优化呢?注意到每次是算一个区间内话费是多少,因此可以用前缀和思路优化,先预处理出1号0点0分到这个月中任何一个时刻的话费,每个月中有多少个不同是时间呢?31 * 1440,不是很大
- 前缀和计算话费时,算的是
i - 1
时刻,% 1440表示在一个周期中是第几分钟,/ 60表示这是第几个小时 sum[i]
表示 00 : 00 − 00 : 01 , 00 : 01 − 00 : 02 , . . . , − 00 : i 00:00-00:01,00:01-00:02,...,-00:i 00:00−00:01,00:01−00:02,...,−00:i,所以利用前缀和时不用-1- 关于如何处理on和off关系,就是把它们全部存在一个容器中,然后按时间顺序排序,如果第一个是on且它的下一个刚好是off,就是一对合法的时间
- 需要记录的是每个人名对应的记录结构题,记录结构题中存对应映射的时刻,状态,标准化时间
语法 :
- 如何在字符串中格式化写入信息,用
sprintf
- map遍历天然就是字典序;且遍历时可以加&,这样就不会涉及到复制,会快一些
- 给每个人对应的时间排序时,可以在结构体中重载,外面只要直接sort即可
- 把string输出成char数组,用
.c_str
,可以返回这个string的char数组的指针 - 注意在结构体内写重载运算符后面还有一个const
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <cstring>
using namespace std;
#define pb push_back
#define fi first
#define se second
const int N = 1010, M = 1440 * 31 + 10;
int cost[24];
double sum[M];
struct Record
{
int minutes;
string state;
string format_time;
bool operator<(const Record &t) const
{
return minutes < t.minutes;
}
};
map<string, vector<Record>> persons;
int main()
{
for (int i = 0; i < 24; i ++ ) cin >> cost[i];
for (int i = 1; i < M; i ++ ) sum[i] = sum[i - 1] + cost[(i - 1) % 1440 / 60] / 100.0;
int n; cin >> n;
// string name, state, format_time;
char name[25], state[10], format_time[20];
int month, day, hour, minute;
for (int i = 0; i < n; i ++ )
{
scanf("%s %d:%d:%d:%d %s", name, &month, &day, &hour, &minute, state);
int minutes = (day - 1) * 1440 + hour * 60 + minute;
sprintf(format_time, "%02d:%02d:%02d", day, hour, minute);
persons[name].pb({minutes, state, format_time});
}
for (auto &person : persons)
{
auto name = person.fi;
auto records = person.se;
sort(records.begin(), records.end());
double total = 0;
for (int i = 0; i + 1 < records.size(); i ++ )
{
auto a = records[i], b = records[i + 1];
if (a.state == "on-line" && b.state == "off-line")
{
if (!total) printf("%s %02d\n", name.c_str(), month);
cout << a.format_time << ' ' << b.format_time << ' ';
double c = sum[b.minutes] - sum[a.minutes];
printf("%d $%.2lf\n", b.minutes - a.minutes, c);
total += c;
}
}
if (total) printf("Total amount: $%.2lf\n", total);
}
}
1017 Queueing at Bank (25 分)
题意 :
- 给n个人到的时间和要占用的时间和m个窗口
- 来早了的要排队到开门,来晚了的不算入
- 保证每个一个窗口可以被一个人占用超过1小时
- 求平均等待时间
思路 :
- 对于每个窗口我们只需要知道它是在什么时刻结束了上一个人的占用
- 对于每个人要怎么安排呢?应该是安排到最早结束的那个窗口,所以我们每次要在所有窗口中找到最小值,所以可以用优先队列
- 注意service_time要和60取一个min
做法 :
- 把每个人的到达时间和占用时间都用结构体存下,因为还要根据到达时间遍历,因此结构体中重载运算符
- 把m个窗口安排好,时间都是8点
- 遍历每个人,如果来晚了,cointinue;找到当前结束时间最早最小的窗口,这个人的开始占用时间就是到达时间和最小结束时间中的最大值,总等待时间累加开始时间和到达时间之差,总人数++,将新的结束时间再放入窗口中
语法 :
- 小根堆的定义
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e4 + 10;
int n, m;
struct Person
{
int arrive_time;
int service_time;
bool operator< (const Person &t) const
{
return arrive_time < t.arrive_time;
}
}persons[N];
int main()
{
scanf("%d%d", &n, &m);
int hour, minute, second, service_time, arrive_time;
for (int i = 0; i < n; i ++ )
{
scanf("%d:%d:%d %d", &hour, &minute, &second, &service_time);
service_time = min(service_time, 60);
arrive_time = hour * 3600 + minute * 60 + second;
persons[i] = {arrive_time, service_time * 60};
}
sort(persons, persons + n);
priority_queue<int, vector<int>, greater<int>> windows;
for (int i = 0; i < m; i ++ ) windows.push(8 * 3600);
int sum = 0, cnt = 0;
for (int i = 0; i < n; i ++ )
{
auto person = persons[i];
int w = windows.top();
windows.pop();
if (person.arrive_time > 17 * 3600) break;
int start_time = max(w, person.arrive_time);
sum += start_time - person.arrive_time;
cnt ++ ;
int end_time = start_time + person.service_time;
windows.push(end_time);
}
printf("%.1lf\n", (double)sum / cnt / 60);
}
1026 Table Tennis (30 分)
思路 :
- 考虑每个人,首先分成是否是vip放在两个优先队列内,重要的信息有 到达的时间(转换成秒数),和玩的时间
- 考虑每张桌子,首先分成是否是vip桌子放在两个优先队列内,重要的信息有 编号,上一个人的结束时间
1035 Password (20 分)
- 题意 :输入的每行的第二个字符串中按照题意替换字符,并且记录被替换过的这两个字符串,按要求输出
- 思路 :数组记录即可,因为是按顺序一一对应的
- 语法 :puts和printf在这种时候比较方便。
#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 a, b;
cin >> a >> b;
string c = change(b);
if (b != c) name[m] = a, pwd[m ++ ] = c;
}
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);
}
else
{
cout << m << endl;
for (int i = 0; i < m; i ++ ) cout << name[i] << ' ' << pwd[i] << endl;
}
return 0;
}
1036 Boys vs Girls (25 分)
题意 :
- 找出男孩中分数最高的和女孩中分数最低的,分别输出姓名以及分数差
思路 :
- 发现不需要把他们全部存下来排序,直接输入比较即可
- 更新变量的条件有 1.比待更新变量更优;2.当前待更新变量还没有被赋值
语法 :
string.empty()
表示这个字符串还没有被赋值过string.size()
#include <iostream>
using namespace std;
int main()
{
int n; cin >> n;
string boy_name, boy_id;
int boy_score;
string girl_name, girl_id;
int girl_score;
string name, id, sex;
int score;
for (int i = 0; i < n; i ++ )
{
cin >> name >> sex >> id >> score;
if (sex == "F")
{
if (girl_name.empty() || score > girl_score)
{
girl_score = score;
girl_name = name;
girl_id = id;
}
}
else
{
if (boy_name.empty() || score < boy_score)
{
boy_score = score;
boy_name = name;
boy_id = id;
}
}
}
if (girl_name.empty()) puts("Absent");
else cout << girl_name << ' ' << girl_id << endl;
if (boy_name.empty()) puts("Absent");
else cout << boy_name << ' ' << boy_id << endl;
if (boy_name.empty() || girl_name.empty()) puts("NA");
else cout << girl_score - boy_score << endl;
}
1050 String Subtraction (20 分)
- 题意 :给两个字符串,在第一个字符串中将在第二个中出现过的字符全部删除
- 语法 : g e t l i n e getline getline函数在 i o s t r e a m iostream iostream头文件中;unordered_set的count函数和insert函数
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
string s1, s2;
getline(cin, s1);
getline(cin, s2);
unordered_set<char> hash;
for (auto c : s2) hash.insert(c);
string res;
for (auto c : s1)
if (!hash.count(c))
res += c;
cout << res;
return 0;
}
1060 Are They Equal (25 分)
题意 :
- 给定一个机器能够保存的有效数字的位数,以及两个浮点数,求输出这两个浮点数保留后的形式以及是否相等
思路 :
- 数字不一定标准,可能有前导零(以后这种格式转换类问题最好不要信题目说给float?);题目中提到
d[1]>0 unless the number is 0
,意思是除了0需要特殊处理,否则删去前导零 - 如果数值是0,则指数规定为0
- k就是原先小数点前有几个有效数字的意思,所以我们当然是去找’.’;这种表示方法(不是科学表示法)对小数点后的数字也可能用到,因此找到小数点位置后,后面的数字也可能要用到,就新建立一个字符串,是移除小数点的版本
- 1.找到’.‘的位置,如果没有就在最后加上,且是最后一位;2.新的字符串中去掉原先的’.’;3.移除前导零,同时k–(比如0.0123 * 10 ^ 5,变成0.123 * 10 ^ 4);4.如果移除后发现新字符串空了,说明原先全部是0,k=0;5.如果长度大于n,截断,如果长度小于n,后面补零;6.返回
语法 :
- string中find没找到的话返回-1
- substr只传一个参数,用来截断
#include <iostream>
using namespace std;
string change(string a, int n)
{
int k = a.find('.');
if (k == -1) a += '.', k = a.find('.');
string s = a.substr(0, k) + a.substr(k + 1);
while (s.size() && s[0] == '0') s = s.substr(1), k -- ;
if (s.empty()) k = 0;
if (s.size() > n) s = s.substr(0, n);
else s += string(n - s.size(), '0');
return "0." + s + "*10^" + to_string(k);
}
int main()
{
int n;
string a, b;
cin >> n >> a >> b;
a = change(a, n);
b = change(b, n);
if (a == b) cout << "YES " << a;
else cout << "NO " << a << ' ' << b;
}
1061 Dating (20 分)
- 题意 :captive letter大写字母;case sensitive区分大小写;common共同的
- 题意 :先找到第一二个字符串中第一个一样的且符合条件的大写字母,然后是第二个一样的且符合条件大写字母,然后找第三四个字符串中第一个符合条件且一样的字母
- 思路 :要看清题目背后含义,字母范围;在第一个和第二个的输出中,如果写在同一个循环里,第一个满足后用了bool还不够,还要加一个continue,否则直接进入了第二个判断条件,而采用标准写法用两个while就没有这个问题;
- 语法 :时间题常见用printf的%02d;printf选择句式;ASCII码中从小到大分别是,数字,大写字母,小写字母;或中的与不需要括号;a[k] - ‘A’ + 10
#include <iostream>
using namespace std;
int main()
{
string a, b, c, d;
cin >> a >> b >> c >> d;
int k = 0;
while (true)
{
if (a[k] == b[k] && 'A' <= a[k] && 'G' >= a[k]) break;
k ++ ;
}
char weekdays[7][4] = {"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
printf("%s ", weekdays[a[k] - 'A']);
k ++ ;
while (true)
{
if (a[k] == b[k] && (a[k] >= '0' && a[k] <= '9' || a[k] >= 'A' && a[k] <= 'N')) break;
k ++ ;
}
printf("%02d:", a[k] <= '9' ? a[k] - '0' : a[k] - 'A' + 10);
for (int i = 0;; i ++ )
if (c[i] == d[i] && (c[i] >= 'a' && c[i] <= 'z' || c[i] >= 'A' && c[i] <= 'Z'))
{
printf("%02d", i);
break;
}
return 0;
}
1073 Scientific Notation (20 分)
- 题意 :科学计数法转数字
- 思路 :当科学计数法系数>0时,有可能不仅无法加零,还去不了小数点;注意题目有个关键地方,无论这个数是正负,第一位是符号位
- 语法 :string的find函数;substr若只有一个参数,那个参数是pos,返回包含pos在内后面的整个字符串;string函数,先长度后内容
#include <iostream>
using namespace std;
int main()
{
string s;
cin >> s;
if (s[0] == '-') cout << '-';
int k = s.find('E');
string a = s[1] + s.substr(3, k - 3);
int b = stoi(s.substr(k + 1));
if (b <= 0) cout << "0." << string(- b - 1, '0');
else if (b < a.size() - 1) a = a.substr(0, b + 1) + '.' + a.substr(b + 1);
else a += string(b - a.size() + 1, '0');
cout << a;
return 0;
}
1077 Kuchiguse (20 分)
- 题意 :给n个字符串,找这n个字符串最长的公共后缀( 2 < = N < = 100 2<=N<=100 2<=N<=100, m a x l e n < = 256 maxlen <= 256 maxlen<=256)
- 语法 :与getline搭配使用getchar!reverse在algorithm中;substr只传一个参数的妙用,后缀
#include <iostream>
using namespace std;
const int N = 110;
string s[N];
int main()
{
int n;
cin >> n;
getchar();
for (int i = 0; i < n; i ++ ) getline(cin, s[i]);
for (int k = s[0].size(); k; k -- )
{
string sf = s[0].substr(s[0].size() - k);
bool is_matched = true;
for (int i = 1; i < n; i ++ )
if (s[i].size() < k || s[i].substr(s[i].size() - k) != sf)
{
is_matched = false;
break;
}
if (is_matched)
{
cout << sf;
return 0;
}
}
cout << "nai";
return 0;
}
1082 Read Number in Chinese (25 分)
题意 :
1084 Broken Keyboard (20 分)
- 题意 :The English letters must be capitalized英文字母必须大写
- 题意 :给两个字符串,找第二个字符串中在第一个中没有出现的字符,无视大小写
- 思路 :双指针即可,b中有的a中一定有,反之不一定,且按顺序,利用好这种性质,a为主指针,将b中与a一一比对;为了防止数组越界,要在b中最后加上一个a中没有出现过的字符
- 语法 :char = toupper(char);ASCII码才127之间,且可以直接以char当数组下标;让一个数组全部都是某个具体数值的方法
#include <iostream>
using namespace std;
int main()
{
string a, b;
cin >> a >> b;
bool st[200] = {0};
b += '#';
for (int i = 0, j = 0; i < a.size(); i ++ )
{
char x = toupper(a[i]), y = toupper(b[j]);
if (x == y) j ++ ;
else
{
if (!st[x]) st[x] = true, cout << x;
}
}
return 0;
}
1108 Finding Average (20 分)
- 题意 :number和numbers
- 题意 :输入n个字符串,判断分别是否是合法数字(大小,小数点后位数,无效字符)
- 思路 :看小数点后是否合法时,如果点在最后一位,num.size() - k就会是1,在倒数第二位,就会是2,倒数第三位等于3,在倒数第三位时后面有两位有效数字,所以大于三,就多于两位有效数字
- 语法 :stof(float实数)可以帮我们判断输入字符串是否合法,如果不合法会抛出异常,所以只要写try,catch语句,如果是合法数字,再判断是否在正负一千以内,小数点后是否最多两位;但这个函数有一个问题5.2abc也被算作合法数字,它会看这个字符串的前几位能否构成合法实数, 如果能,它就只用前几位,所以这里还要判断最终位数。size_t是记录用了几个字符,如果用的字符的数量小于num.size,说明就是5.2abc形式,也不是合法的;**catch(…)**表示接收任何类型的异常;string.find ,k!=-1就是存在小数点;string.c_str();且以上这些不需要其它额外的头文件
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
double sum = 0;
int cnt = 0;
while (n -- )
{
string num;
cin >> num;
double x;
bool success = true;
try
{
size_t idx;
x = stof(num, &idx);
if (idx < num.size()) success = false;
}
catch(...)
{
success = false;
}
if (x < -1000 || x > 1000) success = false;
int k = num.find('.');
if (k != -1 && num.size() - k > 3) success = false;
if (success) cnt ++ , sum += x;
else printf("ERROR: %s is not a legal number\n", num.c_str());
}
if (cnt > 1) printf("The average of %d numbers is %.2lf", cnt, sum / cnt);
else if (cnt == 1) printf("The average of 1 number is %.2lf", sum);
else printf("The average of 0 numbers is Undefined");
return 0;
}
1124 Raffle for Weibo Followers (20 分)
- 题意 :给n个字符串,输出从开始位置每隔m个位置的字符串,要跳过已经被输出过的
- 思路 :模拟就是从第一个开始的位置输出,然后每次跳到n个位置后,如果跳到的位置已经被输出过,那就跳过;索性用数组记录位置对应的字符串,就不用for了;用set判断是否已经被输出过以及有没有输出过字符串。
#include <iostream>
#include <unordered_set>
using namespace std;
const int N = 1e3 + 10;
string name[N];
int main()
{
int m, n, s;
cin >> m >> n >> s;
unordered_set<string> se;
for (int i = 1; i <= m; i ++ ) cin >> name[i];
int k = s;
while (k <= m)
{
if (se.find(name[k]) != se.end()) k ++ ;
else
{
cout << name[k] << endl;
se.insert(name[k]);
k += n;
}
}
if (se.empty()) cout << "Keep going...";
return 0;
}
1141 PAT Ranking of Institutions (25 分)
语法 :
- 注意本题的数据对精度的要求比PAT高;因为本题中有除法,可能除不尽,由于我们算的不是总分/1.5,而是一个乙级读进来就/1.5,这样精度会有一些欠缺
- 本题对精度要求较高,为了避免出现真实的总分是124,但用浮点数存储的是123.999999999的情况,建议在将整数取整之前加上eps,比如eps可以取 1 0 − 8 10^{-8} 10−8。
- 将输入的字符串转小写
#include <iostream>
#include <cstring>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
struct School
{
string name;
int cnt;
double sum;
School(): cnt(0), sum(0) {}
bool operator< (const School &t) const
{
if (sum != t.sum) return sum > t.sum;
if (cnt != t.cnt) return cnt < t.cnt;
return name < t.name;
}
};
int main()
{
int n;
cin >> n;
unordered_map<string, School> hash;
while (n -- )
{
string id, sch;
double grade;
cin >> id >> grade >> sch;
for (auto& c : sch) c = tolower(c);
if (id[0] == 'B') grade /= 1.5;
else if (id[0] == 'T') grade *= 1.5;
hash[sch].sum += grade;
hash[sch].cnt ++ ;
hash[sch].name = sch;
}
vector<School> schools;
for (auto item : hash)
{
item.second.sum = (int)(item.second.sum + 1e-8);
schools.push_back(item.second);
}
sort(schools.begin(), schools.end());
cout << schools.size() << endl;
int rank = 1;
for (int i = 0; i < schools.size(); i ++ )
{
auto s = schools[i];
if (i && s.sum != schools[i - 1].sum) rank = i + 1;
printf("%d %s %d %d\n", rank, s.name.c_str(), (int)s.sum, s.cnt);
}
return 0;
}
1153 Decode Registration Card of PAT (25 分)
思路 :
输出按人数非递增顺序,格式为 考场编号 总人数。若人数并列则按考场编号递增顺序输出。
,因此for (auto item : hash) rooms.push_back({-item.second, item.first});
,因此可以直接sort(rooms.begin(), rooms.end());
,输出的时候printf("%s %d\n", room.second.c_str(), -room.first);
#include <iostream>
#include <cstring>
#include <unordered_map>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 10010;
int n, m;
struct Person
{
string id;
int grade;
bool operator< (const Person &t) const
{
if (grade != t.grade) return grade > t.grade;
return id < t.id;
}
}p[N];
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> p[i].id >> p[i].grade;
for (int k = 1; k <= m; k ++ )
{
string t, c;
cin >> t >> c;
printf("Case %d: %s %s\n", k, t.c_str(), c.c_str());
if (t == "1")
{
vector<Person> persons;
for (int i = 0; i < n; i ++ )
if (p[i].id[0] == c[0])
persons.push_back(p[i]);
sort(persons.begin(), persons.end());
if (persons.empty()) puts("NA");
else
for (auto person : persons) printf("%s %d\n", person.id.c_str(), person.grade);
}
else if (t == "2")
{
int cnt = 0, sum = 0;
for (int i = 0; i < n; i ++ )
if (p[i].id.substr(1, 3) == c)
{
cnt ++ ;
sum += p[i].grade;
}
if (!cnt) puts("NA");
else printf("%d %d\n", cnt, sum);
}
else
{
unordered_map<string, int> hash;
for (int i = 0; i < n; i ++ )
if (p[i].id.substr(4, 6) == c)
hash[p[i].id.substr(1, 3)] ++ ;
vector<pair<int, string>> rooms;
for (auto item : hash) rooms.push_back({-item.second, item.first});
sort(rooms.begin(), rooms.end());
if (rooms.empty()) puts("NA");
else
for (auto room : rooms)
printf("%s %d\n", room.second.c_str(), -room.first);
}
}
return 0;
}
柚131: 好牛的思路
菜腿1994: 博主准备考研了吗?什么专业
菜腿1994: 大佬买的哪本书看的,推荐下
ALangiu: 宝藏博主,继续更新,好详细好喜欢
青衣素人: 为什么处理 1 1 1 -1 -1 1 数据所需的次数为一次? 这个不用考虑方向的依据没看懂。按照题意不是一次只能一个方向,难不成是回旋镖。。。