【力扣刷题笔记第四期】系统设计题

一、招聘策略

某次招聘活动中,共有 deptNum 个部门在招人,每个部门都设定了自己的:招聘目标人数、机考和技面的最低分要求。共有 candidateNum 个应聘者,每个应聘者都有两项能力值:机考分数和技面分数。

招聘规则如下:

  • 选人规则:首先达到部门的门槛要求,即机考和技面分数均大于等于对应最低分要求;然后选人:优先录取技面分数较高的;若技面分数相同,则录取机考分数较高的;若机考分数也相同,则录取应聘者编号较小的。
  • 先进行常规录取,此阶段采用轮询录取方式,以完成招聘目标:
    • 每一轮按部门编号从小到大轮询,部门遵照选人规则录取一人,若无符合要求者则放弃;若已招满(或已无人可录),不再选人;
    • 不断轮询,直至所有部门都完成招聘目标(但不能超过)、或未招满但已无人可录。
  • 在常规录取阶段结束后,各部门均尝试进行一次补录,但最多补录一人(即可能比招聘目标多一人):
    • 按部门编号从小到大的顺序进行补录;
    • 如果还有应聘者与该部门常规录取的最后一名应聘者能力相同(即机考和技面分数均相同),则部门参照选人规则补录一人即结束招聘(即使未成功补录,也结束招聘)。

现给出部门的招聘需求deptDemands,以及应聘者的信息 candidateAbilities。请按部门编号从小到大依次输出所录取人员,部门内所录取人员按录取顺序输出(若无录取人员,输出空序列 [])。

输入

第一行的整数表示招聘部门个数 deptNum,1 <= deptNum <= 10
随后 deptNum 行依次表示每个部门的要求,记录于 deptDemands 数组中,deptDemands[i] 表示编号为 i 的部门的目标人数 机考最低分 技面最低分,目标人数范围 [1, 200],最低分范围 [100, 150]

接下来一行的整数表示应聘者数量 candidateNum,1 <= candidateNum <= 10000
随后 candidateNum 行依次表示每个应聘者的信息,记录于数组 candidateAbilities 中,candidateAbilities[j] 表示编号为 j 的应聘者的机考分数 技面分数,机考和技面分数取值范围 [100, 200]

输出

按部门编号从小到大,每行输出一个部门所录取人员的编号;若没有录取人员,输出空序列 [] 。

样例

输入样例 1 

2
2 130 120
1 150 150
6
150 100
160 190
150 200
200 190
160 190
160 190

输出样例 1

[2 1 4]
[3]

提示样例 1

招聘录取过程解释如下:

  • 第一轮:按部门编号从小到大,部门0 先选人,按选人规则优先录取了应聘者2 。然后部门1选人,优先录取了应聘者 3 ,部门1招满、退出轮询。
  • 第二轮:部门0 选人,应聘者1、4、5 的能力值都相同,录取编号小的应聘者1 。

    两轮后部门0 和 部门1 都达到人数目标,完成常规录取。

  • 补录:部门0 先选人,应聘者 4 和 5 与此前最后录取的应聘者1 的能力值相同,按规则补录一人(应聘者4)后结束招聘。 然后部门1 补录,没有人与此前最后录取的应聘者3 具备相同的能力值。

招聘结束后:部门0依次录取人员为 2 1 4,部门1依次录取人员为 3 (应聘者0 和 应聘者5 未被任何部门录取)

from typing import List
class DeptDemand:
    def __init__(self, dept_id, employ_num, progm_thd, tech_thd):
        self.dept_id = dept_id
        self.employ_num = employ_num
        self.progm_thd = progm_thd
        self.tech_thd = tech_thd
        # 记录录取人员
        self.employ_list = []
class Candidate:
    def __init__(self, candidate_id, progm_score, tech_score):
        self.candidate_id = candidate_id
        self.progm_score = progm_score  # 机考分数
        self.tech_score = tech_score  # 面试分数
class Solution:
    def __init__(self):
        self.finish = True
        self.temp = -1
    def recruitment_result(self, dept_demands: List[DeptDemand],
                           candidate_abilities: List[Candidate]) -> List[List[int]]:
        candidate_abilities.sort(key=lambda x: (x.tech_score, x.progm_score, -x.candidate_id), reverse=True)
        # 招聘池不再变化结束
        while len(candidate_abilities) != self.temp:
            self.temp = len(candidate_abilities)
            for dept_demand in dept_demands:
                if len(dept_demand.employ_list) < dept_demand.employ_num:  # 部门没招满
                    for num, candidate in enumerate(candidate_abilities):
                        if candidate.progm_score >= dept_demand.progm_thd and candidate.tech_score >= dept_demand.tech_thd:
                            dept_demand.employ_list.append(candidate_abilities.pop(num))
                            break
        # 补录环节
        for dept_demand in dept_demands:
            for num, candidate in enumerate(candidate_abilities):
                if dept_demand.employ_list:
                    last = dept_demand.employ_list[-1]
                    if candidate.progm_score == last.progm_score and candidate.tech_score == last.tech_score:
                        dept_demand.employ_list.append(candidate_abilities.pop(num))
                        break
                else:
                    continue
        return [[y.candidate_id for y in x.employ_list] for x in dept_demands]
  • candidate_abilities.sort(key=lambda x: (x.tech_score, x.progm_score, -x.candidate_id), reverse=True)  记一下,list的排序,
  • while len(candidate_abilities) != self.temp 表示在 candidate_abilities 的长度发生变化时继续循环。只有当 candidate_abilities 的长度不再变化时,循环才会终止。
  • dept_demand.employ_list  遍历,可以直接.employ_list 
  • return [[y.candidate_id for y in x.employ_list] for x in dept_demands] 

这是一个列表推导式,用于构建返回结果。

外层 for x in dept_demands 遍历每个部门。

内层 [y.candidate_id for y in x.employ_list] 遍历每个部门的 employ_list,提取每个被录取候选人的 candidate_id,并生成一个包含这些 ID 的列表。

  • break 语句被执行时,它会退出当前的内层 for 循环(遍历 candidate_abilities 的循环)。然而,退出这个内层循环后,程序会继续执行外层的 for 循环的下一次迭代,即检查下一个 dept_demand。但需要注意的是,虽然程序会继续检查下一个 dept_demand,但是由于外层的 while 循环会重新进行检查和调整,最终还是会确保每个部门尽量满足其需求。

先满足第一个部门的招第一个人,在满足第二个部门招第一个人,再招第一个部门的剩下的人

  • 这道题比较特殊,函数的入参是一个list【类】,遍历列表,列表的其中一项(比如dept_demand)又可以打点引用这个类里面的参数

二、SQL解析执行

请设计一个简易数据库系统,支持三个命令:

  • CREATE(int tableId, int colNum, string keys) :创建一个表,其id为tableId;如果该表已经存在,则不做任何处理。
    • colNum 为该表列的数量,列名由单字母表示,并从字母 a 开始、按 a-z 顺序编号,例如colNum=3,表示有三列,列名分别为字母 a b c;
    • keys 表示该表的主键(指的是一列或多列的组合,其值能唯一地标识表中的每一行),keys中每个字符代表一列,如 keys=ba表示主键为b列和a列的组合。
  • INSERT(int tableId, int[] values) :添加一条记录:

    • values 每个元素按顺序一一对应每列的值。
    • 若主键冲突,则不做任何处理。
  • SELECT(int tableId, string[] conditions) :根据条件查询记录,并按主键升序输出结果集:

    • conditions 每个元素代表一个条件,格式如a=25,条件仅为等于(不存在大于、小于等情况);conditions 各元素间仅为 and 关系。
    • 主键升序:按keys中列出现的顺序依次进行排序,每列按值大小升序输出。如keys=ba,先按b列升序排序,若b列值相同则进一步按a列升序排序。

现给出一批命令语句,请按输入顺序解析与执行,并输出每个SELECT 命令的结果集(CREATE 和 INSERT 命令无输出)

输入

首行为整数 num, 表示其后输入的命令行数,取值范围[1,30000]。
之后每行为一条命令,3种命令的输入格式分别为:

  • CREATE 命令,格式为:CREATE:tableId,colNum,keys
    • tableId 取值范围[1,10000]。
    • colNum 取值范围[1,26]。
    • keys 为仅由小写字母组成的字符串,每个字符都不重复且在列名范围内,长度范围[1,26]。
  • INSERT 命令,格式为:INSERT:tableId,value1 value2 ...
    • value 的取值范围 [0,10000]
  • SELECT 命令,格式为:SELECT:tableId,condition1 and condition2 ...
    • 每个condition 的格式如a=25
    • 查询条件有若干个,小于等于 colNum,且不重复。

用例保证 INSERT 和 SELECT命令中tableId所代表的表是已经创建的
每种命令最多10000条命令

输出

逐个输出每个 SELECT 命令的结果集,每行输出表中所有列的值、且按列的顺序输出。
如果某个SELECT命令的查询结果为空,此SELECT输出empty

注:CREATE 和 INSERT 的执行结果不需要输出

样例

输入样例 1 

5
CREATE:1,3,a
INSERT:1,2 3 7
INSERT:1,4 5 6
INSERT:1,3 4 6
SELECT:1,b=5 and c=6

输出样例 1

4 5 6

提示样例 1

CREATE:1,3,a 创建一个id为1的数据库表,有3列(列名分别为a,b,c),a列为主键
INSERT:1,2 3 7 向数据库表1插入一条记录,其值分别为 2 3 7 (即a=2,b=3,c=7)
INSERT:1,4 5 6 向数据库表1插入一条记录,其值分别为 4 5 6
INSERT:1,3 4 6 向数据库表1插入一条记录,其值分别为 3 4 6
SELECT:1,b=5 and c=6 查询数据库表1的内容,条件是b=5并且c=6,符合要求的记录只有4 5 6 这一条

from typing import List

class Solution:
    def init(self):
        self.db = dict()
    
    def create(self, table_id: int, col_num: int, keys: str) -> None:
        """在此添加你的代码"""
        if table_id in self.db:
            return
        self.db[table_id] = {
            "keys":[ord(c)-ord("a") for c in keys],
            "save_vlaues":[]
        }
    def insert(self, table_id: int, values: List[int]) -> None:
        """在此添加你的代码"""
        for v in self.db[table_id]["save_vlaues"]:
            flag = True
            for k in self.db[table_id]["keys"]:
                if v[k] != values[k]:
                    flag = False
                    break
            if flag:
                return
        self.db[table_id]["save_vlaues"].append(values)
    def select(self, table_id: int, conditions: List[str]) -> List[int]:
        """在此添加你的代码"""
        check = [(ord(c.split("=")[0])-ord("a"), int(c.split("=")[1])) for c in conditions]
        res = []
        for v in self.db[table_id]["save_vlaues"]:
            flag = True
            for i, x in check:
                if v[i] != x:
                    flag = False
                    break
            if flag:
                res.append(v)
        return sorted(res, key=lambda x:[x[i] for i in self.db[table_id]["keys"]])

if __name__ == "__main__":
    num = int(input().strip())
    function = Solution()
    for _ in range(num):
        com, value = input().strip().split(":")
        if com == "CREATE":
            table_id, col_num, keys = value.split(",")
            function.create(int(table_id), int(col_num), keys)
        elif com == "INSERT":
            table_id, values = value.split(",")
            values = list(map(int, values.split()))
            function.insert(int(table_id), values)
        elif com == "SELECT":
            table_id, conditions = value.split(",")
            conditions = conditions.split(" and ")
            results = function.select(int(table_id), conditions)
            if not results:
                print('empty')
            else:
                for result in results:
                    print(str.join(" ", map(str, result)))
  • 在一些情况下,可以使用 ord() 函数将字母转换为索引。例如,将小写字母 'a' 转换为 0,将 'b' 转换为 1:

index_a = ord('a') - ord('a') # 0

index_b = ord('b') - ord('a') # 1

index_c = ord('c') - ord('a') # 2

  • insert函数
    • 遍历 self.db[table_id]["save_vlaues"] 中已保存的所有值(即表中的所有行)。对于每一行,初始化 flagTrue,表示假设当前行的键值组合与插入的值匹配。
    • 遍历 self.db[table_id]["keys"] 中的每一个键:
      • 比较当前行 v 中键 k 对应的值与插入的值 values 中键 k 对应的值。
      • 如果发现任意一个键值不匹配,将 flag 设为 False 并跳出内层循环。
      • 如果 flag 仍然为 True,说明当前行的所有键值都与插入的值匹配。这意味着表中已经存在一行具有相同键值组合的记录,此时直接返回,不插入新的值。

这样做的目的是为了避免在表中插入重复的键值组合,从而保持键值组合的唯一性。

假设我们有一个表,键是 ab,已有的数据如下:

[ [1, 2, 3], [4, 5, 6] ]

如果我们尝试插入 [1, 2, 7],由于键值组合 a=1, b=2 已经存在,插入操作将会被跳过。如果我们尝试插入 [4, 5, 8],同理,由于键值组合 a=4, b=5 已经存在,插入操作也会被跳过。

这样可以保证表中的键值组合是唯一的。

三、简易的文件系统

模拟设计实现一套简易的文件系统,支持以下操作:

  • FileSystem() — 系统初始化,当前目录在根目录 /,根目录下为空。
  • makeDir(string dirName) — 在当前目录下创建一个新的目录,若目录名dirName和当前目录下的文件名或目录名重复,则创建失败,返回 false; 否则创建成功,返回 true。
  • createFile(string fileName) — 在当前目录下创建一个新的文件。若文件名fileName和当前目录下的文件名或目录名重复,则创建失败,返回 false; 否则创建成功,返回 true。
  • changeDir(string pathName) — 切换当前目录到pathName

    • 首先,若 pathName 为"",当前目录保持不变,返回 true;
    • 其次,若目录存在则切换成功,并返回 true;否则,直接返回 false 。

      注:pathName 以绝对路径 /xx/yy/zz 或者相对路径 xx/yy/zz 给出。

  • remove(string name) — 删除当前目录下名称为name的文件或目录。

    • 若存在则删除成功,并返回 true;否则,直接返回 false 。
    • 注:删除目录时,除删除目录本身外,还需删除该目录下的所有子目录和文件。
  • listDir() — 返回当前目录下的目录和文件。目录在前、文件在后,各自按字典序升序。
    • 注:返回结果不含子目录下的内容

  • 输入
  • 每行表示一个函数调用,初始化函数仅首行调用一次,累计函数调用不超过 1000 次。
    • fileName、dirName、name 仅含小写字母,长度范围皆为 [1, 100]
      pathName 仅含小写字母和 /, 0 <= pathName.length < 1000,注:不包含 .. 或者.

      注:pathName 不为 “” 时为合法目录名,末尾可能带一个 /

      输出

      答题时按照函数/方法原型中的要求(含返回值)进行实现,输出由框架完成(其中首行固定输出 null)

      样例

      输入样例 1 

      FileSystem()
      makeDir("dirc")
      makeDir("dirb")
      makeDir("dirc")
      listDir()
      changeDir("dirc/")
      createFile("fileb")
      makeDir("dirb")
      createFile("dirb")
      listDir()
      changeDir("/dirb/dirc")
      

      输出样例 1

      null
      true
      true
      false
      ["dirb", "dirc"]
      true
      true
      true
      false
      ["dirb", "fileb"]
      false
      

      提示样例 1

      FileSystem() // null
      makeDir(“dirc”) // true
      makeDir(“dirb”) // true
      makeDir(“dirc”) // false,重复
      listDir() // [“dirb”, “dirc”],注意:目录名末尾不要额外加 /
      changeDir(“dirc/“) // true,末尾带了一个 / ,等同于目录 dirc
      createFile(“fileb”) // true
      makeDir(“dirb”) // true
      createFile(“dirb”) // false,重复
      listDir() // [“dirb”, “fileb”]
      changeDir(“/dirb/dirc”) // false,目录不存在

from typing import List

class FileSystem:
    def __init__(self):
        self.root = {"name":'/',"type":'dir', "content":{}}
        self.curr = self.root
        
    def make_dir(self, dir_name: str) -> bool:
        if self.curr["content"].get(dir_name):
            return False
        
        self.curr["content"][dir_name] = {"name":dir_name,"type":'dir', "content":{}}
        return True
       
    def create_file(self, file_name: str) -> bool:
        if self.curr["content"].get(file_name):
            return False
        
        self.curr["content"][file_name] = {"name":file_name,"type":'file'}
        return True
           
    def change_dir(self, path_name: str) -> bool:
        if path_name == '':
            return True
        
        paths = path_name.split('/')
        
        curr = self.root if path_name.startswith('/') else self.curr
        
        for path in paths:
            if not path:
                continue
            if not curr["content"].get(path) or curr["content"][path]["type"] != 'dir':
                return False
            curr = curr["content"][path]
        
        self.curr = curr
        return True
                    
    def remove(self, name: str) -> bool:
        if not self.curr["content"].get(name):
            return False
        
        del self.curr["content"][name]
        return True

    def list_dir(self) -> List[str]:
        dirs = []
        files = []
        
        for name,value in self.curr["content"].items():
            if value["type"] == 'file':
                files.append(name)
            elif value["type"] == 'dir':
                dirs.append(name)
            
        return sorted(dirs)+sorted(files)

字典的嵌套使用,经典题目

四、版本满足度

现约定软件版本号格式为三段x.y.z,x段表示主版本,y段表示次要版本,z段表示修订版本,y段、z段是可选的(即可以为 x.y 或 x),每段的值范围是 [0,999]之间的整数。

软件的兼容、依赖等管理中,通常需要用到「版本范围」,一种「版本范围」的表示方法为前缀x.y.z,说明如下:

  • 前缀为=>>=<<=,分别表示等于、大于、大于等于、小于、小于等于给定版本。若未给定y段或z段,则表示 y=0 或 z=0
  • 前缀为~,表示的版本范围如下:

    1. 如果给定了次要版本,则从该版本到下一个次要版本(不包括下一个次要版本)。若未给定z段,则表示 z=0
      如 ~1.9 次要版本9的下一次要版本是10,表示范围为 [1.9.0, 1.10.0) 即 [1.9.0, 1.9.999]
      如 ~1.9.5 表示 [1.9.5, 1.10.0) 即 [1.9.5, 1.9.999]
      如 ~1.0.0 表示 [1.0.0, 1.1.0) 即 [1.0.0, 1.0.999]

    2. 如果未给定次要版本(即仅有主版本),则从该版本到下一个主版本(不包括下一个主版本)。未给定的y段和z段均为 0
      如 ~1 表示 [1.0.0, 2.0.0) 即 [1.0.0, 1.999.999];注意它与 ~1.0.0 或 ~1.0 是不同的

现给出多对「版本范围」;对于每一对输入的两个范围rangeA rangeB,如果存在交集(即存在版本同时满足两个范围)输出字符串yes,否则输出no

输入样例 1 

4
>1.0.0 <=1.0.0
<1.2.0 >1.1.999
~0.10 =0.10.0
=99.0 ~99

输出样例 1

no
no
yes
yes

提示样例 1

输入有四对版本范围,依次判断如下:

  • >1.0.0表示版本范围为 [1.0.1,999.999.999],<=1.0.0表示版本范围为 [0.0.0,1.0.0]。第一个范围的左值 1.0.1 大于第二个范围的右值 1.0.0,因此两个范围不存在交集
  • <1.2.0表示范围 [0.0.0,1.1.999];>1.1.999的z段已经是最大值999,相当于 >=1.2.0,因此表示范围为 [1.2.0,999.999.999]。第一个范围的右值小于第二个范围的左值,因此两个范围不存在交集
  • ~0.10给定了次要版本,表示范围为 [0.10.0, 0.11.0) 即 [0.10.0, 0.10.999],=0.10.0表示范围 [0.10.0, 0.10.0],版本 0.10.0 同时在两个版本范围内,存在交集
  • ~99仅有主版本(未给定次要版本),表示范围为 [99.0.0, 100.0.0) 即 [99.0.0, 99.999.999],版本 99.0.0 同时在两个版本范围内,存在交集

版本号可以使用字符串、三元组、整数等方式来表示和比较

"""
Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved.
Description: 上机编程认证
Note: 缺省代码仅供参考,可自行决定使用、修改或删除
"""

from typing import List
import re

class Version:
    def __init__(self, version: str):
        self.min = 0
        self.max = 999999999
        matched = re.match(r'([=<>~]+)(\d+).?(\d+)?.?(\d+)?', version)
        if matched:
            self.op = matched.group(1)
            self.major = int(matched.group(2))
            self.minor = matched.group(3)
            self.step = matched.group(4)
            op = {
                '=': self.process_eq,
                '<': self.process_less,
                '<=': self.process_less,
                '>': self.process_great,
                '>=': self.process_great,
                '~': self.process_normal
            }.get(self.op)
            if op:
                op()
        else:
            raise ValueError("Invalid version string format")

    @staticmethod
    def _convert_to_int(number):
        if number is not None:
            return int(number)
        return 0

    @staticmethod
    def _convert_to_calc_number(major, minor, step):
        minor = Version._convert_to_int(minor)
        step = Version._convert_to_int(step)
        return major * 1000000 + minor * 1000 + step

    @staticmethod
    def _convert_to_upper_number(major, minor, step):
        if minor is None:
            minor = 999
        else:
            minor = int(minor)
        step = 999
        return major * 1000000 + minor * 1000 + step

    def process_eq(self):
        self.min = Version._convert_to_calc_number(self.major, self.minor, self.step)
        self.max = self.min

    def process_less(self):
        self.max = Version._convert_to_calc_number(self.major, self.minor, self.step)
        if '=' not in self.op:
            self.max -= 1

    def process_great(self):
        self.min = Version._convert_to_calc_number(self.major, self.minor, self.step)
        if '=' not in self.op:
            self.min += 1

    def process_normal(self):
        self.min = Version._convert_to_calc_number(self.major, self.minor, self.step)
        self.max = Version._convert_to_upper_number(self.major, self.minor, self.step)

    def have_version(self, another: 'Version') -> str:
        if self.max < another.min or self.min > another.max:
            return 'no'
        return 'yes'

def check(num: int, range_pairs: List[List[str]]) -> List[str]:
    results = []
    for range_pair in range_pairs:
        version1 = Version(range_pair[0])
        version2 = Version(range_pair[1])
        result = version1.have_version(version2)
        results.append(result)
    return results

if __name__ == "__main__":
    num = int(input().strip())
    range_pairs = [list(map(str, input().strip().split())) for _ in range(num)]
    results = check(num, range_pairs)
    print("\n".join(results))

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值