【CCF CSP】202206-3 角色授权 (解题思路+满分题解)

解题思路

首先根据题目背景信息定义结构体 。

一个角色包含下列要素:

  • 名称,是一个字符串,用于唯一标识一个角色;
  • 操作清单,是一个数组,包含一个或多个操作,表示该角色允许执行的操作集合;
  • 资源种类清单,是一个数组,包含一个或多个资源种类,表示该角色允许操作的资源的种类集合;
  • 资源名称清单,是一个数组,包含若干个资源名称,表示该角色允许操作的资源的名称集合。
struct info_ch   //角色自身权限
{
    unordered_set<string> mani_lst;   //操作清单集合
    unordered_set<string> res_type;   //资源种类描述集合
    unordered_set<string> res_name;   //资源名称描述集合
};

角色名字我们后续会存入到map容器的键值中。

然后我们根据样例描述按规范定义变量,读入角色权限相关信息

int n, m, q;        //角色数量、角色关联数量、待检查的操作数量
cin >> n >> m >> q;

map<string, info_ch> role;   //角色名称-角色权限映射
for (int i = 0; i < n; i++)
{
	string chr;              //角色名称
	info_ch chr_info;        //包含权限信息的结构体

	cin >> chr;

	int nv;             
	cin >> nv;
	for (int iv = 0; iv < nv; iv++)       //读入操作
	{
		string mani;
		cin >> mani;
		chr_info.mani_lst.insert(mani);
	}

	int no;
	cin >> no;
	for (int io = 0; io < no; io++)       //读入资源种类
	{
		string type;
		cin >> type;
		chr_info.res_type.insert(type);
	}

	int nn;
	cin >> nn;
	for (int in = 0; in < nn; in++)       //读入资源名称
	{
		string name;
		cin >> name;
		chr_info.res_name.insert(name);
	}
	role.insert(make_pair(chr, chr_info));//最后将(chr,chr_info)存入角色映射
}

 然后是存入用户-角色关联信息,这个是本道题的难点。一开始我想要根据题目样例的顺序用以下的数据结构存入关联信息。

struct info_u     //用户名单
{
    set<string> U;
};

struct info_g     //用户组名单
{
    set<string> G;
};

map<string, temp_u> rel_u;      //对每一个角色维护一个用户名单集合             
map<string, temp_g> rel_g;      //以及一个用户组集合
info_u temp_u;
info_g temp_g;
set<string> U;
set<string> G;

这样读入就会非常简单 

for(int i = 0; i < m; i++)
{
	string chr;
	int ns;
	cin >> chr >> ns;

	for(int is = 0; is < ns; is++)
	{
		string type, user, group;
		cin >> type;
		if(type == "u")
		{
			cin >> user;
			temp_u.U.insert(user);
		}
		else
		{
			cin >> group;
			temp_g.G.insert(group);
		}
	}
	rel_g.insert(make_pair(chr, temp_g));
	rel_u.insert(make_pair(chr, temp_u));
}

但是很遗憾,接下来对每一个用户遍历完所有关联角色就会变得非常低效,因为我们需要遍历全部角色的全部名单,时间复杂度为O(n^2),很可能超时,况且实现这样的功能非常麻烦。于是其实有一个更加简单的算法:对每一个用户维护一个角色名单,这样在遍历查找的时候时间复杂度会下降到O(n);基于这个思路的实现代码如下:

multimap<string, string> rel_u;    //用multimap储存单条关联信息
multimap<string, string> rel_g;    //multimap允许重复键值

for (int i = 0; i < m; i++)
{
	string chr;
	int ns;
	cin >> chr >> ns;

	for (int is = 0; is < ns; is++)
	{
		string type, user, group;
		cin >> type;
		if (type == "u")
		{
			cin >> user;
			rel_u.insert(make_pair(user, chr));  
        }    //multimap在读入的时候会自动将键值相同的用户名放在相邻位置
		else
		{
			cin >> group;
			rel_g.insert(make_pair(group, chr));
		}
	}
}

接下来根据题目要求编写判断授权函数

int check(const info_ch& chr, const string& mani, const string& type, const string& name)
{
    if (chr.mani_lst.count(mani) == 0 && chr.mani_lst.count("*") == 0)
        return 0;
    if (chr.res_type.count(type) == 0 && chr.res_type.count("*") == 0)
        return 0;
    if (chr.res_name.count(name) == 0 && !chr.res_name.empty())
        return 0;
    return 1;
}

这里经测试,本题count方法(4.734s)比find方法(4.718s)要快。并且进行只读操作时比起直接传入参数(直接超时70分),使用引用传递(const "type" &)效率更高。

接下来读入待检查操作,调用判断函数对操作进行判断

for (int i = 0; i < q; i++)
{
	string id, mani, type, name;
	int ng;

	cin >> id >> ng;
	string gr_lst[405];   //用户组名单
	for (int ig = 0; ig < ng; ig++)
		cin >> gr_lst[ig];
	cin >> mani >> type >> name;

	auto range_u = rel_u.equal_range(id);   //对应用户id的角色指针范围
	for (auto it = range_u.first; it != range_u.second; it++)
	{
		const string& rolename = it->second;              //角色名称
		const info_ch& temp = role.find(rolename)->second;//角色权限
		if (check(temp, mani, type, name) == 1)           //检查
		{
			cout << 1 << endl;
			goto ending;
		}
	}

	for (int ig = 0; ig < ng; ig++)
	{
		auto range_g = rel_g.equal_range(gr_lst[ig]);
		for (auto it = range_g.first; it != range_g.second; it++)
		{
			const string& rolename = it->second;
			const info_ch& temp = role.find(rolename)->second;
			if (check(temp, mani, type, name) == 1)
			{
				cout << 1 << endl;
				goto ending;
			}
		}
	}
	cout << 0 << endl;
ending:
	continue;
}

 满分题解

#include <iostream>
#include <string>
#include <map>
#include <set>
#include <unordered_set>

using namespace std;

struct info_ch
{
    string character;
    unordered_set<string> mani_lst;
    unordered_set<string> res_type;
    unordered_set<string> res_name;
};

int check(const info_ch& chr, const string& mani, const string& type, const string& name)
{
    if (chr.mani_lst.count(mani) == 0 && chr.mani_lst.count("*") == 0)
        return 0;
    if (chr.res_type.count(type) == 0 && chr.res_type.count("*") == 0)
        return 0;
    if (chr.res_name.count(name) == 0 && !chr.res_name.empty())
        return 0;
    return 1;
}

int main()
{
    int n, m, q;
    cin >> n >> m >> q;

    map<string, info_ch> role;
    for (int i = 0; i < n; i++)
    {
        string chr;
        info_ch chr_info;

        cin >> chr;

        int nv;
        cin >> nv;
        for (int iv = 0; iv < nv; iv++)
        {
            string mani;
            cin >> mani;
            chr_info.mani_lst.insert(mani);
        }

        int no;
        cin >> no;
        for (int io = 0; io < no; io++)
        {
            string type;
            cin >> type;
            chr_info.res_type.insert(type);
        }

        int nn;
        cin >> nn;
        for (int in = 0; in < nn; in++)
        {
            string name;
            cin >> name;
            chr_info.res_name.insert(name);
        }

        role.insert(make_pair(chr, chr_info));
    }

    multimap<string, string> rel_u;
    multimap<string, string> rel_g;

    for (int i = 0; i < m; i++)
    {
        string chr;
        int ns;
        cin >> chr >> ns;

        for (int is = 0; is < ns; is++)
        {
            string type, user, group;
            cin >> type;
            if (type == "u")
            {
                cin >> user;
                rel_u.insert(make_pair(user, chr));
            }
            else
            {
                cin >> group;
                rel_g.insert(make_pair(group, chr));
            }
        }
    }

    for (int i = 0; i < q; i++)
    {
        string id, mani, type, name;
        int ng;

        cin >> id >> ng;
        string gr_lst[405];
        for (int ig = 0; ig < ng; ig++)
            cin >> gr_lst[ig];
        cin >> mani >> type >> name;

        auto range_u = rel_u.equal_range(id);
        for (auto it = range_u.first; it != range_u.second; it++)
        {
            const string& rolename = it->second;
            const info_ch& temp = role.find(rolename)->second;
            if (check(temp, mani, type, name) == 1)
            {
                cout << 1 << endl;
                goto ending;
            }
        }

        for (int ig = 0; ig < ng; ig++)
        {
            auto range_g = rel_g.equal_range(gr_lst[ig]);
            for (auto it = range_g.first; it != range_g.second; it++)
            {
                const string& rolename = it->second;
                const info_ch& temp = role.find(rolename)->second;
                if (check(temp, mani, type, name) == 1)
                {
                    cout << 1 << endl;
                    goto ending;
                }
            }
        }
        cout << 0 << endl;
ending:
        continue;
    }
    return 0;
}

70分是因为没有用const&引用,90分是因为没有用unordered_set。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Andy_Xie007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值