一、上机打卡
1.1 乒乓球
1.1.1 题目
国际乒联主席沙拉拉自从上任以来就立志于推行一系列改革,以推动乒乓球运动在全球的普及。其中11分制改革引起了很大的争议,有一部分球员因为无法适应新规则只能选择退役。明明就是其中一位,他退役之后走上了乒乓球研究工作,意图弄明白11分制和21分制对选手的不同影响。在开展他的研究之前,明明首先需要对他多年比赛的统计数据进行一些分析,所以需要你的帮忙。 (注:11(21)分制,在一局比赛中,选手A先得到11(21)分且此时领先选手B 2分或2分以上时,则选手A赢得此局;若当双方打成10(20)平后,则先多得2分的一方为胜方,赢得此局。)
明明通过以下方式进行分析,首先将比赛每个球的胜负列成一张表,然后分别计算在11分制和21分制下,双方的比赛结果(截至记录末尾)。一局比赛的开始比分为0比0。 比如现在有这么一份记录,(其中W表示明明获得一分,L表示明明的对手获得一分):
WWWWWWWWWWWWWWWWWWWWWWLW
在11分制下,此时比赛的结果是明明第一局11比0获胜,第二局11比0获胜,正在进行第三局,当前比分1比1。
在21分制下,此时比赛结果是明明第一局21比0获胜,正在进行第二局,当前比分2比1。
再如有这么一份记录,(其中W表示明明获得一分,L表示明明的对手获得一分):
WLWLWLWLWLWLWLWLWLWLWLWLWL
在11分制下,此时比赛的结果是明明和对手打成13比13,这局比赛仍没有分出胜负,因为任何一方都没有领先其对手2分。
在21分制下,此时比赛的结果是明明和对手打成13比13,这局比赛仍在进行中。
由于明明参加过多年的比赛,比赛的数据量相当庞大,如果仅仅使用手工统计,在短时间内统计出结果对于明明来说是相当困难的。因此明明求助于你,希望你能写一个程序,帮助他快速地统计出结果来。
明明的问题可以归结为:给你一系列的比赛数据(WL形式),分别按照11分制和21分制的比赛规则进行统计,然后输出统计结果。
你写的程序要求从标准输入设备中读入测试数据作为你所写程序的输入数据。标准输入设备中有多组测试数据,每行包括一串有W、L和E组成比赛结果,其中W表示明明得一分,L表示明明的对手得一分,E表示该组测试数据的结束,也就是说E后面的W、L应该忽略,无需处理。每行的长度不会超过30个字符。每组测试数据与其后一组测试数据之间没有任何空行,第一组测试数据前面以及最后一组测试数据后面也都没有任何空行。
对于每一组测试数据,你写的程序要求计算出一组相应的运算结果,并将每组运算结果作为你所写程序的输出数据依次写入到标准输出设备中。
每组运算结果由两部分组成,其中第一部分是11分制下的结果,第二部分是21分制下的结果,两部分之间由一个空行分隔。
每部分由若干行组成,每一行对应一局比赛的比分(按比赛信息输入顺序),每局的比分按如下形式表示:m:n,其中m表示明明的得分,n表示明明的对手的得分,m、n之间用一个冒号隔开。
输出时,每组运算结果与其后一组运算结果之间有一个空行,第一组运算结果前面以及最后一组运算结果后面没有任何空行或其他任何字符。 注:通常,显示屏为标准输出设备。
输入样例
WWWWWWWWWWLLLLLLLLLLL
WWWWWWWWWLLLLLLLLLE
LLLLLLLLLLLLLLLLLE
输出样例
13:11
6:9
19:20
0:11
0:6
0:17
1.1.2 总结
首先通过逐字符读取的方式过滤出有效比赛记录,将跨行的胜负序列转化为统一的逻辑流,并以特定字符E作为模拟的终止边界。
针对11分制和21分制分别建立独立的状态机模型,通过维护两个得分计数器实时追踪明明及其对手的比分变化。每一分产生后,系统会立即执行双重约束判定逻辑,即只有当某一方得分达到设定的门槛值且双方分差不小于2分时,才判定当前对局结束并触发输出与计数器清零操作。
cpp
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
using namespace std;
// 模拟得分逻辑并输出
void solve(const string& s, int scoreLimit) {
int w = 0, l = 0;
for (char c : s) {
if (c == 'W') w++;
else if (c == 'L') l++;
// 判胜条件:得分达到上限且分差大于等于2
if ((w >= scoreLimit || l >= scoreLimit) && abs(w - l) >= 2) {
cout << w << ":" << l << endl;
w = 0; l = 0;
}
}
// 输出最后一局(即使是0:0)
cout << w << ":" << l << endl;
}
int main() {
string totalData = "";
string line;
bool endOfAll = false;
// 持续读取直到发现 'E'
while (cin >> line) {
for (char c : line) {
if (c == 'E') {
endOfAll = true;
break;
}
if (c == 'W' || c == 'L') {
totalData += c;
}
}
if (endOfAll) break;
}
// 11分制
solve(totalData, 11);
cout << endl; // 分隔11分制和21分制
// 21分制
solve(totalData, 21);
return 0;
}
1.2 字符串统计
1.2.1 题目
明明最近在做一个有关字符串的统计工作。两个由小写字母组成的字符串s1和s2,明明需要统计出以下四种关系:
(1)在s1或s2中存在的字母(包括在s1和s2中都存在的字母);
(2)在s1中且在s2中的字母;
(3)在s1中但不在s2中的字母,在s2中但不在s1中的字母;
(4)不在s1中且也不在s2中的字母;
例如两个字符串s1为"lkjsvoahs",s2为"qglhskjdfg":
(1)在s1或者在s2或者s1、s2中都存在的字母:adfghjkloqsv;
(2)在s1中且在s2中的字母:hjkls;
(3)在s1中但不在s2中的字母,在s2中但不在s1中的字母:adfgoqv;
(4)不在s1中且也不在s2中的字母:bceimnprtuwxyz;
明明统计了很久,但是由于统计过程十分繁琐,且很容易出错,导致明明的进度非常慢,很有可能因为统计不完而错过了晚上的约会。因此明明想请你帮个忙,帮他写一个程序,用程序来统计出以上几项内容。
明明的问题可以归结为:
输入两串由小写字母组成的字符串s1和s2,比较其中的字母,输出以下四项,输出的字母以字典顺序排列:
(1)在s1或s2中存在的字母(包括在s1和s2中都存在的字母);
(2)在s1中且在s2中的字母;
(3)在s1中但不在s2中的字母,在s2中但不在s1中的字母;
(4)不在s1中且也不在s2中的字母;
例如字符串s1为sadf,s2为asdf,则需输出以下四行(注意输出的格式):
in s1 or s2:adfs
in s1 and s2:adfs
in s1 but not in s2 ,or in s2 but not in s1:
not in s1 and s2:bceghijklmnopqrtuvwxyz
你写的程序要求从标准输入设备中读入测试数据作为你所写程序的输入数据。标准输入设备中有多组测试数据,每组测试数据两行,每组测试数据的第一行为字符串s1,每组测试数据的第二行为字符串s2;s1和s2都由小写英文字母组成,且长度不超过26个字符。测试数据与其后一组测试数据之间没有任何空行,第一组测试数据前面以及最后一组测试数据后面也都没有任何空行。
对于每一组测试数据,你写的程序要求计算出一组相应的运算结果,并将这一组运算结果作为你所写程序的输出数据依次写入到标准输出设备中。
每组运算结果由四行组成:
第一行为在s1或者在s2或者s1、s2中都存在的字母;
第二行为在s1中且在s2中的字母;
第三行为在s1中但不在s2中的字母,在s2中但不在s1中的字母;
第四行为不在s1中且也不在s2中的字母;
具体格式请参考样例输出。
每组运算结果其行首和行尾都没有任何空格,每组运算结果与其后一组运算结果之间有一个空行,最后一组运算结果后面没有空行。
注:通常,显示屏为标准输出设备。
输入样例
sadf
asdf
lkjsvoahs
qglhskjdfg
输出样例
in s1 or s2:adfs
in s1 and s2:adfs
in s1 but not in s2 ,or in s2 but not in s1:
not in s1 and s2:bceghijklmnopqrtuvwxyz
in s1 or s2:adfghjkloqsv
in s1 and s2:hjkls
in s1 but not in s2 ,or in s2 but not in s1:adfgoqv
not in s1 and s2:bceimnprtuwxyz
1.2.2 总结
首先通过构建两个长度为26的布尔型数组作为哈希表,分别对字符串s1和s2中出现的字符进行存在性标记,从而将非结构的字符串比对问题转化为有序集合的成员判定。
在输出阶段,算法通过一次性遍历从小到大排列的字母全集,利用位逻辑运算实现四种集合关系的求解。
1.2.3 代码
cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void solve() {
string s1, s2;
bool firstCase = true;
// 处理多组数据
while (cin >> s1 >> s2) {
// 每组运算结果之间有一个空行
if (!firstCase) cout << endl;
firstCase = false;
vector<bool> inS1(26, false);
vector<bool> inS2(26, false);
// 标记 s1 出现的字母
for (char c : s1) inS1[c - 'a'] = true;
// 标记 s2 出现的字母
for (char c : s2) inS2[c - 'a'] = true;
// (1) 并集
cout << "in s1 or s2:";
for (int i = 0; i < 26; ++i) {
if (inS1[i] || inS2[i]) cout << (char)('a' + i);
}
cout << endl;
// (2) 交集
cout << "in s1 and s2:";
for (int i = 0; i < 26; ++i) {
if (inS1[i] && inS2[i]) cout << (char)('a' + i);
}
cout << endl;
// (3) 对称差
cout << "in s1 but not in s2 ,or in s2 but not in s1:";
for (int i = 0; i < 26; ++i) {
if (inS1[i] != inS2[i]) cout << (char)('a' + i); // 逻辑异或关系
}
cout << endl;
// (4) 补集
cout << "not in s1 and s2:";
for (int i = 0; i < 26; ++i) {
if (!inS1[i] && !inS2[i]) cout << (char)('a' + i);
}
cout << endl;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
solve();
return 0;
}
1.3 隐藏口令
1.3.1 题目
有时候程序员有很奇怪的方法来隐藏他们的口令。
Billy"Hacker"Geits会选择一个字符串S(由L个小写字母组成,5<=L<=100,000),然后他把S顺时针绕成一个圈。
如字符串cbadfa,绕成一个圈后,我们认为字符串首尾相连。
每次取其中一个字母作为起始字母,并顺时针依次取字母而组成一个字符串。这样将得到一些字符串。
比如字符串cbadfa,按照以上规则取出的字符串有:
cbadfa badfac adfacb dfacba facbad acbadf
我们找到最小的那个字符串,可知为acbadf,也可知道它的第一个字符'a'在原字符串cbadfa中为第6个字符(位置从1开始),
将得到的结果6减1得到5,这就是我们需要的口令。
再比如字符串alabala,绕成一个圈后,每次取其中一个字母作为起始字母,并顺时针依次取字母而组成一个字符串。这样将得到一些字符串:
alabala labalaa abalaal balaala alaalab laalaba aalabal
我们找到最小的那个字符串,可知为aalabal,它的第一个字母'a'在原字符串中位置为7,7-1=6,则6为口令。
注:如果按照规则有两个字符串都是最小的,则取前面那一个。
第一行:一个数L
第二行及之后:字符串S。
注意:字符串S可跨多行,但其中的'\n'不算成S中的字符
一行,为得到的口令。
无多余空格或空行。
输入样例
6
cbadfa
输出样例
5
1.3.2 总结
利用双指针同构最小表示法进行线性优化模拟。针对长度为L的循环字符串,算法并非暴力枚举所有移位可能,而是通过维护两个候选起始指针i与j并引入匹配偏移量k,在逻辑拼接的循环流中进行贪心筛选。
比较过程中,一旦在偏移位k处发现字符不等,算法利用字典序的单调性:若当前i起始的子串在该位较大,则判定i到i+k之间的位置均不可能是最小表示的起点,从而将指针i直接跳跃至i+k+1处,反之则移动j。
1.3.3 代码
cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
int L;
if (!(cin >> L)) return 0;
string s = "", temp;
// 跨行读取处理:拼接所有输入行直到达到长度 L
while (s.length() < L && cin >> temp) {
s += temp;
}
// 最小表示法核心逻辑
int i = 0, j = 1, k = 0;
while (i < L && j < L && k < L) {
// 比较循环同构位点字符
char a = s[(i + k) % L];
char b = s[(j + k) % L];
if (a == b) {
k++;
} else {
if (a > b) i = i + k + 1;
else j = j + k + 1;
if (i == j) i++; // 确保两个指针不重合
k = 0; // 重置匹配长度
}
}
// 结果为两个候选指针中的较小者(即原串中位置靠前者)
cout << min(i, j) << endl;
return 0;
}
二、翻译打卡
P1
数字计算机不是一台单独的机器,而是一个由五个不同元素组成的系统:(1)中央处理器;(2)输入设备;(3)存储设备;(4)输出 设备;(5)通信网络,称为总线,它将系统的所有元素连接起来,并将系统连接到外部世界。
P2
程序是一系列指令,告诉计算机硬件对数据执行什么操作。程序可以内置在硬件中,也可以以软件的形式独立存在。在一些专业或"专用"计算机中,操作指令被嵌入其电路中;常见的例子是微型计算机,如计算器、腕表、汽车发动机和微波炉中使用的微型计算机。另一方面,通用计算机虽然包含一些内置程序(在只读存储器中)或指令(在处理器芯片中),但要依靠外部程序来执行有用的任务。一旦计算机被编程,它所能做的就只取决于控制它的软件在任何特定时刻允许它做什么。广泛使用的软件包括各种应用程序------告诉计算机如何执行各种任务的指令。
P3
目前,人们正在积极研究利用许多有前景的新技术制造计算机,例如光计算机、DNA计算机、神经计算机和量子计算机。大多数计算机都是通用计算机,能够计算任何可计算函数,其性能仅受内存容量和运行速度的限制。然而,不同设计的计算机可以针对特定问题提供非常不同的性能;例如,量子计算机可以非常快速地破解某些现代加密算法(通过量子因数分解)。
三、单词打卡
