CSP202206C.角色授权

今天我们来看CSP202206C.角色授权这道题目

题意分析

本题要求实现一个基于角色的访问控制(RBAC)模型。系统中有三类实体:

  1. 角色(Role):包含名称、允许的操作清单、允许的资源种类清单、允许的资源名称清单
  2. 角色关联(Role Association):将角色与用户或用户组关联
  3. 待授权行为(Authorization Request):包含用户信息、操作、资源信息

判断规则:

  • 角色能否执行某操作:操作、资源种类、资源名称都要匹配(支持通配符*,资源名称清单为空表示不限制)
  • 用户能否执行某操作:找到所有与该用户或所属用户组关联的角色,任一角色允许即可

解题思路

1. 数据结构设计

  • 用结构体存储角色信息,使用 unordered_set 存储操作、资源种类、资源名称,便于快速查找
  • unordered_map 建立角色名称到索引的映射
  • 用两个 unordered_map 分别存储:
    • 用户名 → 关联的角色索引列表
    • 用户组名 → 关联的角色索引列表

2. 核心算法

角色匹配判断

  • 检查操作是否在角色的操作清单中,或清单包含 *
  • 检查资源种类是否在角色的资源种类清单中,或清单包含 *
  • 检查资源名称是否在角色的资源名称清单中,或清单为空(不限制)

用户权限判断

  • 收集用户直接关联的所有角色
  • 收集用户所属用户组关联的所有角色(需要去重)
  • 遍历所有关联角色,任一角色允许该操作则返回允许

3. 关键点

  • 独立判断:每个待授权行为独立判断,不存储历史信息
  • 去重:一个用户可能通过多个途径关联到同一角色,需要去重避免重复判断
  • 通配符处理* 表示匹配所有,空资源名称清单表示不限制
  • 性能优化:使用哈希表存储和查找,保证 O(1) 的平均查找时间

4. 时间复杂度

  • 每个角色的存储:O(nv + no + nn)
  • 每次查询:O(关联角色数量),最坏 O(n)
  • 总体:O(q × n),在数据范围内可行

代码实现

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

using namespace std;

// 角色结构体
struct Role {
    string name;
    unordered_set<string> ops;          // 操作清单
    unordered_set<string> res_types;    // 资源种类清单
    unordered_set<string> res_names;    // 资源名称清单(空表示不限制)
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);  // 加速输入输出

    int n, m, q;
    cin >> n >> m >> q;

    vector<Role> roles(n);
    unordered_map<string, int> roleIdx;  // 角色名到索引的映射

    // 读取角色信息
    for (int i = 0; i < n; ++i) {
        Role &r = roles[i];
        cin >> r.name;
        roleIdx[r.name] = i;

        // 读取操作清单
        int nv;
        cin >> nv;
        for (int j = 0; j < nv; ++j) {
            string op;
            cin >> op;
            r.ops.insert(op);
        }

        // 读取资源种类清单
        int no;
        cin >> no;
        for (int j = 0; j < no; ++j) {
            string rt;
            cin >> rt;
            r.res_types.insert(rt);
        }

        // 读取资源名称清单
        int nn;
        cin >> nn;
        for (int j = 0; j < nn; ++j) {
            string rn;
            cin >> rn;
            r.res_names.insert(rn);
        }
        // 如果 nn = 0,res_names 保持空,表示不限制
    }

    // 构建用户/组到角色的映射
    unordered_map<string, vector<int>> userToRoles;
    unordered_map<string, vector<int>> groupToRoles;

    // 读取角色关联
    for (int i = 0; i < m; ++i) {
        string roleName;
        cin >> roleName;
        int idx = roleIdx[roleName];  // 获取角色索引

        int ns;
        cin >> ns;
        for (int j = 0; j < ns; ++j) {
            string type, name;
            cin >> type >> name;
            if (type == "u") {
                userToRoles[name].push_back(idx);
            } else {  // type == "g"
                groupToRoles[name].push_back(idx);
            }
        }
    }

    // 处理每个待授权的行为
    for (int query = 0; query < q; ++query) {
        string userName;
        int ng;
        cin >> userName >> ng;

        // 读取用户所属用户组
        vector<string> groups(ng);
        for (int i = 0; i < ng; ++i) {
            cin >> groups[i];
        }

        string op, resType, resName;
        cin >> op >> resType >> resName;

        // 标记匹配的角色(去重)
        vector<char> matched(n, 0);
        vector<int> matchedRoles;
        matchedRoles.reserve(n);

        // 处理用户直接关联的角色
        auto itUser = userToRoles.find(userName);
        if (itUser != userToRoles.end()) {
            for (int idx : itUser->second) {
                if (!matched[idx]) {
                    matched[idx] = 1;
                    matchedRoles.push_back(idx);
                }
            }
        }

        // 处理用户组关联的角色
        for (const string &g : groups) {
            auto itGroup = groupToRoles.find(g);
            if (itGroup != groupToRoles.end()) {
                for (int idx : itGroup->second) {
                    if (!matched[idx]) {
                        matched[idx] = 1;
                        matchedRoles.push_back(idx);
                    }
                }
            }
        }

        // 检查是否有任意匹配的角色允许该操作
        bool allowed = false;
        for (int idx : matchedRoles) {
            const Role &r = roles[idx];
            
            // 检查操作是否匹配
            bool opOK = r.ops.count("*") || r.ops.count(op);
            
            // 检查资源种类是否匹配
            bool typeOK = r.res_types.count("*") || r.res_types.count(resType);
            
            // 检查资源名称是否匹配(空清单表示不限制)
            bool nameOK = r.res_names.empty() || r.res_names.count(resName);
            
            // 三个条件都满足则允许
            if (opOK && typeOK && nameOK) {
                allowed = true;
                break;
            }
        }

        cout << (allowed ? 1 : 0) << '\n';
    }

    return 0;
}

总结

本题是典型的 RBAC(基于角色的访问控制)模型实现,主要考察:

  1. 数据结构设计 :合理使用哈希表(unordered_set/unordered_map)存储和查找数据
  2. 逻辑判断:准确理解并实现三层判断逻辑(用户→角色→权限)
  3. 边界处理 :正确处理通配符*、空清单、去重等特殊情况
  4. 性能优化:使用哈希表保证查询效率,避免超时

关键是要理清判断的层次关系:先确定用户关联的所有角色,再逐个检查这些角色的权限是否覆盖当前操作。使用哈希表能够高效地处理大量查询。

相关推荐
m0_734571768 分钟前
深入理解5g <二十> dmrs
开发语言·5g
码匠许师傅40 分钟前
【C++三方组件】cxxopts:轻量首选的命令行解析
c++
金金计较.41 分钟前
Go语言-4
开发语言·golang
hansang_IR1 小时前
【讲解】CSP-S2026 第一轮初赛
c++·算法
Methy1 小时前
IoTHub半个月崩了五次?都是裸rethrow惹的祸
服务器·c++·后端
泡海椒1 小时前
jquick-pdf 表格行列尺寸控制实战:单元格合并的可行边界
java·开发语言·pdf
我不会起名字3221 小时前
一天一道力扣Hot100(36):深度优先算法---组合总和
数据结构·c++·后端·python·算法·go
纪念 2291 小时前
c++类和对象(四)
开发语言·c++
汉克老师1 小时前
GESP2026年9月认证C++六级( 第三部分编程题(2、分树规划))精讲
c++·gesp·小学生·学c++编程
学生小羊2 小时前
C++ 初阶 学习博客
c语言·c++·c++与c语言的区别·c++基础学习