问题描述:
201612-3权限查询
大概题意:
- 权限:有不分等级权限与分等级权限。分等级权限给出最高权限。
- 角色:具有多个权限
- 用户:具有多个角色,角色之间的权限有可能重复,分等级权限取最高等级。
有q个询问:给出用户和对应权限,判断该用户是否有对应权限,若是分等级权限,则给出该用户拥有的最高等级。
大致思路:
按题目要求,根据输入处理权限、角色和用户。
我们需要根据用户查找到角色,再根据角色查找对应的权限。
询问是判断用户与权限的关系。因此,在处理用户的时候,不存储对应的角色,而是参照输入的角色名,查找对应的权限,并存储下来。使得用户与权限直接对应。
大模拟题重在思考如何存储。
- 权限:结构体
struct Privilege{
string pName;//权限类名
mutable int maxLevel;//权限对应的最高等级,若为不分等级的权限,值为-1
//mutable 是为了maxLevel可更新,若删去会报错
bool operator<(const Privilege &w)const{//当多个权限需要排序时,按照pName升序排序
return pName<w.pName;
}
}
- 角色集合与用户集合:unordered_map
经过前面的分析,角色与用户都是存储名字(一个string)与权限(多个Privilege)之间对应关系。
- 多个Privilege:根据题意需要实现去重。可以使用
set<Privilege>
来存储权限集合。
set可以用二叉树搜索树实现,set有两个特点:
- set中的元素不允许重复;
- set内部会维护一个严格的弱排序关系。
- 名字(一个string)与权限(多个Privilege)之间的对应:且根据名字需要快速查找相应的权限,可以使用
unordered_map<string, set<Privilege>>
来存储角色集合/用户集合。
unordered_map底层采用哈希表的存储结构,特点如下:
- 和pair类似,用key:value的形式存储数据,key互不相同且不能修改,当关键字不是int类型时,非常方便;
- 与map相比,unordered_map顾名思义是无序的
- 查找时间复杂度O(1);
OK~上代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <unordered_map>
using namespace std;
struct Privilege{
string pName;
mutable int maxLevel;//值-1表示不分等级
bool operator< (const Privilege &w)const{
return pName<w.pName;
}
};
unordered_map<string,set<Privilege>> role,user;
Privilege getPrivilege(string str){//权限的构造函数
Privilege pr;
int t=str.find(':');
if(t==-1){//不分等级
pr.pName=str;
pr.maxLevel=-1;
}
else{//分等级
pr.pName=str.substr(0,t);
pr.maxLevel=str[t+1]-'0';
}
return pr;
}
int main(){
int n;
string str;
string name;
int cnt;
/*处理权限:
由题意得角色对应的权限列表一定是合法的,
则用户只需要根据角色定义的权限查询即可,
因此权限读入之后用不到,因此不需要存储
*/
cin>>n;
while(n--) cin>>str;
/*处理角色*/
cin>>n;
while(n--){
cin>>name>>cnt;
while(cnt--){
cin>>str;
Privilege p=getPrivilege(str);
set<Privilege> &r=role[name];//表示该角色对应权限的集合
if(p.maxLevel==-1)//当前权限不分等级,直接添加
r.insert(p);
else{//当前权限分等级
if(!r.count(p))//该角色中目前还没有该权限
r.insert(p);
else{//该角色已有该权限
auto it=r.find(p);
it->maxLevel=max(it->maxLevel,p.maxLevel);
}
}
}
}
/*处理用户*/
cin>>n;
while(n--){
cin>>name>>cnt;
set<Privilege> &u=user[name];//该用户对应权限的集合
while(cnt--){
cin>>str;
set<Privilege> &ps=role[str];
for(Privilege p:ps){//对输入角色拥有的权限逐一判断
if(p.maxLevel==-1)//当前权限不分等级,直接添加
u.insert(p);
else{//当前权限分等级
if(!u.count(p))//该用户中目前还没有该权限
u.insert(p);
else{//该用户已有该权限
auto it=u.find(p);
it->maxLevel=max(it->maxLevel,p.maxLevel);
}
}
}
}
}
/*处理询问*/
cin>>n;
while(n--){
cin>>name;
cin>>str;
if(!user.count(name)){//用户不存在
cout<<"false"<<endl;
continue;
}
Privilege q=getPrivilege(str);
set<Privilege> &u=user[name];
if(!u.count(q)){//该用户无该权限类型
cout<<"false"<<endl;
}
else{
auto it=u.find(q);
if(it->maxLevel==-1){//该权限不分等级
cout<<"true"<<endl;
}
else{
if(q.maxLevel==-1){//询问不带等级的分等级权限
cout<<it->maxLevel<<endl;
}
else{
if(it->maxLevel>=q.maxLevel) cout<<"true"<<endl;
else cout<<"false"<<endl;
}
}
}
}
return 0;
}
一些小Tips
- struct、set和unordered_map的使用场景和使用方法(上面已经提到啦~)
- find() 函数的运用:
find()是STL容器中非常常见的操作。作用也非常好理解,就查找容器中的元素。
在本题中使用到:
(1) string :
str.find(str2)
当str2是str子串时,返回在str中第一次出现的地址(下标);如果在str中找不到str2,则返回string::npos
,也就是-1
;
str.find(str2,pos)
从str的pos号位置开始匹配,返回结果同上;
(2) set :
find(value)
返回set中对应值为value的迭代器,如set<int>::iterator it = st.find(2);
更多时候方便简写成auto it=st.find(2);
(3) unordered_map/map :
与set类似,返回的也是迭代器 - substr() 函数的运用:
substr(pos,len)
返回从pos号位开始,长度为len的子串;
若len省略,则是从pos号位开始到结尾结束的子串;
在字符串处理,尤其是一行当中有多个元素需要分隔处理的时候,非常有帮助!
参考了一些
[1]. Acwing(安利一波y总大佬🙏 讲题很清晰 也好懂~)
[2]. 《算法笔记》