#P2894. 第1题-物料回收计算器
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 658
            Accepted: 165
            Difficulty: 4
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2025年4月23日-暑期实习(留学生)
                              
                      
          
 
- 
                        算法标签>字符串          
 
第1题-物料回收计算器
题目分析
我们有 N 台服务器的物料编码,每个编码中分别包含一段代表 CPU 型号的子串(格式为C后跟两位数字)、一段代表内存型号的子串(M+两位数字)、一段代表主板型号的子串(B+两位数字)。每个编码中,这三类备件可能出现多次,但我们只取各自第一次出现的型号;然后统计所有服务器下线后,可回收的不同型号及对应数量,按型号字典序输出。
核心问题是:如何从字符串中高效提取固定格式子串,并统计。
解题思路
- 
模式匹配
- 对于每个物料编码字符串,使用正则表达式或手动扫描:
- CPU 型号:
C\d{2} - 内存型号:
M\d{2} - 主板型号:
B\d{2} 
 - CPU 型号:
 - 从左到右找第一个匹配,分别得到三个型号。
 
 - 对于每个物料编码字符串,使用正则表达式或手动扫描:
 - 
计数统计
- 使用字典(或哈希表、
map)记录每种型号出现的次数。 - 处理完所有编码后,字典中键为型号,值为数量。
 
 - 使用字典(或哈希表、
 - 
结果输出
- 对每类备件,将型号按字符串升序排序后输出,格式为:
型号1,数量1;型号2,数量2;... - 若只有一个型号,则不加分号。
 
 - 对每类备件,将型号按字符串升序排序后输出,格式为:
 
相关算法
- 正则匹配:时间复杂度与字符串长度成线性关系。
 - 哈希统计:插入与查找均摊 O(1)。
 - 排序:对 K 种型号排序需 O(K log K)。
 
复杂度分析
- 对每个编码(长度 L)进行三次正则查找:O(L)。共 N 台服务器,总时间 O(N L)。
 - 最后对每类备件假设有 K 种型号排序:O(K log K)。通常 K ≪ N × L。
 - 总体时间复杂度:O(N L + K log K)。
 - 空间复杂度:O(N L) 用于存储输入,O(K) 用于统计字典。
 
代码实现
Python
import re
import sys
def main():
    data = sys.stdin.read().split()
    n = int(data[0])
    codes = data[1:]
    
    cntCpu = {}
    cntMem = {}
    cntBoa = {}
    
    # 预编译正则
    pCpu = re.compile(r'C\d{2}')
    pMem = re.compile(r'M\d{2}')
    pBoa = re.compile(r'B\d{2}')
    
    for s in codes:
        # 提取第一次匹配
        m = pCpu.search(s)
        cpu = m.group() if m else ''
        m = pMem.search(s)
        mem = m.group() if m else ''
        m = pBoa.search(s)
        boa = m.group() if m else ''
        
        cntCpu[cpu] = cntCpu.get(cpu, 0) + 1
        cntMem[mem] = cntMem.get(mem, 0) + 1
        cntBoa[boa] = cntBoa.get(boa, 0) + 1
    
    # 按型号字典序输出
    def fmt(d):
        items = sorted(d.items())
        return ';'.join(f"{k},{v}" for k, v in items)
    
    print(fmt(cntCpu))
    print(fmt(cntMem))
    print(fmt(cntBoa))
if __name__ == '__main__':
    main()
Java
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(in.readLine().trim());
        String[] codes = in.readLine().trim().split("\\s+");
        
        // TreeMap 保持键有序
        Map<String, Integer> cpuMap = new TreeMap<>();
        Map<String, Integer> memMap = new TreeMap<>();
        Map<String, Integer> boaMap = new TreeMap<>();
        
        Pattern pCpu = Pattern.compile("C\\d{2}");
        Pattern pMem = Pattern.compile("M\\d{2}");
        Pattern pBoa = Pattern.compile("B\\d{2}");
        
        for (String s : codes) {
            Matcher m = pCpu.matcher(s);
            String cpu = m.find() ? m.group() : "";
            m = pMem.matcher(s);
            String mem = m.find() ? m.group() : "";
            m = pBoa.matcher(s);
            String boa = m.find() ? m.group() : "";
            
            cpuMap.put(cpu, cpuMap.getOrDefault(cpu, 0) + 1);
            memMap.put(mem, memMap.getOrDefault(mem, 0) + 1);
            boaMap.put(boa, boaMap.getOrDefault(boa, 0) + 1);
        }
        
        // 输出格式化
        printMap(cpuMap);
        printMap(memMap);
        printMap(boaMap);
    }
    
    // 按要求格式化输出
    static void printMap(Map<String, Integer> map) {
        boolean first = true;
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Integer> e : map.entrySet()) {
            if (!first) sb.append(";");
            sb.append(e.getKey()).append(",").append(e.getValue());
            first = false;
        }
        System.out.println(sb.toString());
    }
}
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<string> codes(n);
    for (int i = 0; i < n; i++) {
        cin >> codes[i];
    }
    map<string, int> cpuMap, memMap, boaMap;
    // 正则模式
    regex rc("C\\d{2}");
    regex rm("M\\d{2}");
    regex rb("B\\d{2}");
    for (auto &s : codes) {
        smatch m;
        // CPU
        regex_search(s, m, rc);
        string cpu = m.size() ? m.str(0) : "";
        // Memory
        regex_search(s, m, rm);
        string mem = m.size() ? m.str(0) : "";
        // Board
        regex_search(s, m, rb);
        string boa = m.size() ? m.str(0) : "";
        cpuMap[cpu]++;
        memMap[mem]++;
        boaMap[boa]++;
    }
    // 输出函数
    auto printMap = [](const map<string,int>& M) {
        bool first = true;
        for (auto &p : M) {
            if (!first) cout << ";";
            cout << p.first << "," << p.second;
            first = false;
        }
        cout << "\n";
    };
    printMap(cpuMap);
    printMap(memMap);
    printMap(boaMap);
    return 0;
}
        题目内容
某云站点有一批服务器待下线,已知这些服务器所对应的物料编码(代表不同服务器型号的标识字符串),特定物料编码本身已包含CPU/内存/主板等备件型号,请计算出这批服务器下线后可回收不同的CPU/内存/主板型号和数量。物科编码只包含大写英文字母和数字,其中一定包含三类备件型号,它的固定格式是一个大写字母+两个数字。字母C代表CPU,M代表内存Memory,B代表主板Board。其他字母和数字可忽略,同类备件存在多个型号时取第一个。
输入描述
第一行为服务器个数 N ,范围是 [1,1000];
第二行为 N 个物料编码字符串,依次用空格隔开。
输出描述
统一按照型号1,数量1;型号2,数量2;型号3,数量3...的格式输出:
第一行为CPU的型号和对应数量;
第二行为内存的型号和对应数量;
第三行为主板的型号和对应数量。
同类备件存在多个型号时按照字典序输出。
样例1
输入
2
C01M23B050130 C01M23B060130
输出
C01,2
M23,2
B05,1;B06,1
样例2
输入
3
C0CM23B05C130X11 C01M23B050130Y22 C01M24B05C130Z33
输出
C01,2;C13,1
M23,2;M24,1
B05,3