2月21日(91-93题)

91.乒乓球

题目描述

给你一系列的比赛数据(WL形式),分别按照11分制和21分制的比赛规则进行统计,然后输出统计结果。

代码

cpp 复制代码
#include <iostream>
#include <string>
#include <cmath>

using namespace std;

// 处理并输出单组测试数据的结果
void process_test_case(const string& s, bool is_first) {
    // 除了第一组数据,后续每组数据前需要加一个空行分隔
    if (!is_first) {
        cout << "\n";
    }
    
    // --- 11 分制统计 ---
    int w = 0, l = 0, games_played = 0;
    for (char c : s) {
        if (c == 'W') w++;
        else if (c == 'L') l++;
        
        // 获胜条件:某方达到 11 分及以上,且分差大于等于 2
        if ((w >= 11 || l >= 11) && abs(w - l) >= 2) {
            cout << w << ":" << l << "\n";
            w = 0; l = 0; // 比分清零
            games_played++; // 记录已经打完的完整局数
        }
    }
    if (w > 0 || l > 0 || games_played == 0) {
        cout << w << ":" << l << "\n";
    }
    
    // 11 分制和 21 分制之间需要一个空行
    cout << "\n";
    
    // --- 21 分制统计 ---
    w = 0; l = 0; games_played = 0;
    for (char c : s) {
        if (c == 'W') w++;
        else if (c == 'L') l++;
        
        // 获胜条件:某方达到 21 分及以上,且分差大于等于 2
        if ((w >= 21 || l >= 21) && abs(w - l) >= 2) {
            cout << w << ":" << l << "\n";
            w = 0; l = 0;
            games_played++;
        }
    }
    // 同理,处理 21 分制的末尾残局
    if (w > 0 || l > 0 || games_played == 0) {
        cout << w << ":" << l << "\n";
    }
}

int main() {
    
    string line;
    string current_s = "";
    bool first_case = true;
    
    // 逐行读取输入
    while (getline(cin, line)) {
        for (char c : line) {
            if (c == 'E') {
                // 遇到 E,说明这组数据读取完毕,开始计算并输出
                process_test_case(current_s, first_case);
                first_case = false;
                current_s = ""; // 清空记录,为下一组数据做准备
                break; // 跳出内部 for 循环,直接忽略本行 E 后面的所有杂乱字符
            } else if (c == 'W' || c == 'L') {
                // 持续累加 W 和 L
                current_s += c;
            }
        }
    }
    
    return 0;
}

总结

核心思路 :本题重点在于状态模拟与字符串处理。需要按字符逐个读取,分别累加胜负分数;当一方达到目标分数(11或21)且分差大于等于2时,立刻结算并清零。此外,还要精准处理多组数据中 E 字符的截断以及严格的空行换行格式。

错误教训 :最大的坑在于边界情况处理 。在上一局完美结束且遇到 E 时多输出了一个 0:0,这是因为忽略了当前 OJ 的特殊要求。

排错心得:核对测试用例时,不要被网页界面的"滚动条"或局部视错觉误导。面对长输出报错,务必将自己代码的完整输出与标准输出进行首尾、逐行对比。


92.字符串统计

明明最近在做一个有关字符串的统计工作。两个由小写字母组成的字符串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

代码

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {

    string s1, s2;
    bool isFirstTestCase = true;

    while (cin >> s1 >> s2) {
        // 控制空行格式:除了第一组,后面的每组输出前都加一个空行
        if (!isFirstTestCase) {
            cout << "\n";
        }
        isFirstTestCase = false;

        // 使用长度为 26 的布尔数组记录字母是否出现
        vector<bool> in1(26, false);
        vector<bool> in2(26, false);

        for (char c : s1) {
            in1[c - 'a'] = true;
        }
        for (char c : s2) {
            in2[c - 'a'] = true;
        }

        string res1 = "", res2 = "", res3 = "", res4 = "";

        // 遍历 a 到 z,天然按字典序处理
        for (int i = 0; i < 26; ++i) {
            char c = 'a' + i;
            bool b1 = in1[i];
            bool b2 = in2[i];

            if (b1 || b2) res1 += c;                         // 在 s1 或 s2 中
            if (b1 && b2) res2 += c;                         // 在 s1 且 s2 中
            if (b1 != b2) res3 += c;                         // 在 s1 不在 s2,或在 s2 不在 s1
            if (!b1 && !b2) res4 += c;                       // 都不在
        }

        cout << "in s1 or s2:" << res1 << "\n";
        cout << "in s1 and s2:" << res2 << "\n";
        cout << "in s1 but not in s2 ,or in s2 but not in s1:" << res3 << "\n";
        cout << "not in s1 and s2:" << res4 << "\n";
    }

    return 0;
}

总结

​ 题目的核心要求是统计小写字母的出现情况,并且要求输出的字母按字典顺序排列 。因为小写英文字母一共只有 26 个,所以可以直接利用两个长度为 26 的布尔数组来分别映射并记录 s1s2 中各个字母是否出现过。

这种"哈希映射"的方法有几个好处:

  1. 自动排序:当索引 从0 到 25(对应字母 'a' 到 'z')遍历时,天然就是字典序,省去了后续排序的步骤。
  2. 逻辑清晰 :四种情况可以直接用布尔逻辑表示:
    • 并集 (情况1):in1[i] || in2[i]
    • 交集 (情况2):in1[i] && in2[i]
    • 对称差集 (情况3):in1[i] != in2[i] (即异或操作,只有一个为真)
    • 补集 (情况4):!in1[i] && !in2[i]

93.隐藏口令

题目描述

有时候程序员有很奇怪的方法来隐藏他们的口令。

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为口令。

注:如果按照规则有两个字符串都是最小的,则取前面那一个。

代码

cpp 复制代码
#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int getMinRepresentation(const string& s) {
   int n = s.length();
   int i = 0, j = 1, k = 0;
   
   while (i < n && j < n && k < n) {
       // 取模运算实现首尾相连的环形效果
       int char_i = s[(i + k) % n];
       int char_j = s[(j + k) % n];
       
       if (char_i == char_j) {
           k++;
       } else {
           if (char_i > char_j) {
               // i 开头的这段被淘汰
               i = i + k + 1;
           } else {
               // j 开头的这段被淘汰
               j = j + k + 1;
           }
           // 保证 i 和 j 错开
           if (i == j) {
               j++;
           }
           // 每次淘汰后,匹配长度 k 清零重新开始
           k = 0;
       }
   }
   // 返回更小的那个下标作为起点
   return min(i, j);
}

int main() {
   
   int L;
   if (!(cin >> L)) return 0;
   
   string s = "";
   string temp;
   while (s.length() < L && cin >> temp) {
       s += temp;
   }
   
   int ans = getMinRepresentation(s);
   cout << ans << "\n";
   
   return 0;
}

总结

​ 具体思路如下:

  • 维护两个指针 ij(代表两个可能成为最小表示起点的候选位置),初始时 i = 0, j = 1。再维护一个匹配长度 k = 0
  • 比较以 ij 为起点,第 k 个字符的大小,即比较 S[(i+k)%L]S[(j+k)%L]
    • 如果相等,说明前 k 个字符都一样,那就继续看下一个,k++
    • 如果 S[(i+k)%L] > S[(j+k)%L] :这意味着以 ii+k任何一个位置 作为起点,都不可能是最小的(因为同样长度下,对应 j 那边的字符更小)。所以我们直接把 i 跳过这段区间,更新为 i = i + k + 1
    • 如果 S[(i+k)%L] < S[(j+k)%L] :同理,以 j 开始的这段区间被淘汰,更新 j = j + k + 1
  • 如果某次跳跃后 i == j 了,为了保证找的是两个不同的起点,让 j++
  • 最后 k 清零,继续新一轮比较。当 ij 跑完一圈,或者 k 达到了 LLL,循环结束。最后 min(i, j) 就是我们要找的最小起点

翻译

​ 现代数字计算机在概念上都是相似的,无论其尺寸大小。然而,根据成本和性能,它们可以被划分为几个类别:个人计算机(PC)或微型计算机,这是一种成本相对较低的机器,通常为台式机大小(尽管"笔记本电脑"小到可以装进公文包,而"掌上电脑"可以装进口袋);工作站,一种具有增强的图形和通信能力的微型计算机,使其特别适用于办公;小型计算机,通常对于个人使用来说过于昂贵,其性能适合企业、学校或实验室;以及大型计算机,这是一种大型且昂贵的机器,能够满足大型商业企业、政府部门、科研机构等的需求(其中最大和最快的被称为超级计算机)。

​ 数字计算机并不是一台单一的机器:相反,它是一个由五个截然不同的部分组成的系统:(1)中央处理器(CPU);(2)输入设备;(3)内存存储设备;(4)输出设备;以及(5)被称为总线的通信网络,它将系统的所有部分连接起来,并将系统与外部世界相连。

relatively: adv. 相对地,比较地

相关推荐
陈天伟教授2 小时前
人工智能应用- 材料微观:03. 微观结构:纳米金
人工智能·神经网络·算法·机器学习·推荐算法
闻缺陷则喜何志丹2 小时前
【数论 等差数列】P9183 [USACO23OPEN] FEB B|普及+
c++·数学·数论·等差数列
拳里剑气2 小时前
C++ 11
开发语言·c++·学习方法
孞㐑¥2 小时前
算法—穷举,爆搜,深搜,回溯,剪枝
开发语言·c++·经验分享·笔记·算法
黄昏晓x2 小时前
C++----异常
android·java·c++
宇木灵2 小时前
C语言基础-九、动态内存分配
c语言·开发语言·学习·算法
追随者永远是胜利者2 小时前
(LeetCode-Hot100)301. 删除无效的括号
java·算法·leetcode·职场和发展·go
楼田莉子2 小时前
Linux网络学习:网络的基础概念
linux·运维·服务器·网络·c++·学习
追随者永远是胜利者2 小时前
(LeetCode-Hot100)239. 滑动窗口最大值
java·算法·leetcode·职场和发展·go