众所周知,互联网时代以来各大公司被“脱裤”的历史是一部五彩缤纷(误)的血泪史,给各大厂商造成了极大的经济损失。更为重要的是,由于有些用户在多个网站使用相同的用户名、密码,一旦一家网站被拖库,用户往往会遭受全方位的损失。为避免此情况,良心企业一般只在数据库中存储用户密码的哈希值——也就是根据特定规则产生的散列值,无法由此倒推出原密码。但这种方法也有一个缺点,即输入不同的密码有极小概率会得到一样的哈希值(我们称之为碰撞),从而被系统认定密码正确!现在你所在的公司采取如下方法产生密码字符串(长度至少为8,只包含大小写字母和数字)的哈希值:
- 不区分字母的大小写,沿用16进制A代表10,B代表11……的规律,将原字符串视为一串36进制的数字
- 将字符串平均划为4块,若无法平均划分,保证在前的分块不短于在后的分块,且长度差不超过1。如:长度26的字符串各分块长度为7、7、6、6,长度13的字符串各分块长度为4、3、3、3
- 将每块的数字加和,取其个位数,四块取出的四个36进制数字顺次连接,得到一个四位36进制数字,即为该密码字符串的哈希值。
然而由于这种方式过于睿智,使得碰撞的几率奇高,你的任务就是为公司防范风险,在碰撞发生的时候给予示警!
输入格式:
第一行一个整数N(N<1000),为操作的个数。
以下N行,每行一个字符、两个字符串(length<100),中间均以空格分隔。字符代表操作类型,两个字符串代表用户名和密码。当字符为L时,代表以该用户名密码尝试登录;
当字符为R时,代表尝试注册这组用户名、密码,若注册成功则记录在案。
输出格式:
N行,对于每一个L(登录操作),若密码正确,则输出一行“Success!”;
若密码错误或用户不存在,则输出一行“Failed!”;
若密码错误但会通过哈希检测而被放行,则输出一行“Attention!”。
对于每一个R(注册操作),若已存在该用户名,则输出一行“Repeated!”;
否则注册成功,输出一行“Signed!”。
以上输出均不包括引号。
思路:题目有点长,但是读懂了其实很简单,纯大模拟,注意复制输出的时候得去样例复制,题面里的符号是错的。
代码
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
//故人西辞黄鹤楼,烟花三月下扬州。
map<string,string>mp;//存账号密码
map<string,int>has;//存哈希
string ls="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//36进制转换辅助字符串
string ha(string s){//计算哈希的函数
int len = s.length();
int cnt = len*1.0/4;//计算平均要截取多少个
int num = len%4;//计算前几个要多1
int now = 0;
int tmp = 0;
string res;
for(int i=0;i<s.size();i++){
s[i] = char(s[i]-'a' + 'A' );//全部转大写
tmp += ls.find(s[i]);
now++;
if(num>0){
if(now>=cnt+1){
res += ls[tmp%36];
tmp = now = 0;
num--;
}
}else{
if(now>=cnt){
res += ls[tmp%36];
tmp = now = 0;
}
}
}
return res;
}
signed main(){
IOS
int t;cin>>t;
while(t--){
string op,name,pwd;
cin>>op>>name>>pwd;
if(op == "R"){
if(mp.count(name)!=0){
cout<<"Repeated!"<<endl;
}else{
mp[name] = pwd;
string tmp = ha(pwd);
has[tmp]=1;
cout<<"Signed!"<<endl;
}
}else{
if(mp.count(name)==0 || pwd != mp[name]){
string tmp = ha(pwd);
if(has.count(tmp)!=0)cout<<"Attention!"<<endl;
else cout<<"Failed!"<<endl;
}else{
cout<<"Success!"<<endl;
}
}
}
return 0;
}