解题思路
首先根据题目背景信息定义结构体 。
一个角色包含下列要素:
- 名称,是一个字符串,用于唯一标识一个角色;
- 操作清单,是一个数组,包含一个或多个操作,表示该角色允许执行的操作集合;
- 资源种类清单,是一个数组,包含一个或多个资源种类,表示该角色允许操作的资源的种类集合;
- 资源名称清单,是一个数组,包含若干个资源名称,表示该角色允许操作的资源的名称集合。
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。