官网链接TUOJ




题解
快写吐了,一堆小细节。
除2和乘2可以直接使用位运算。
总流程
输入:n
- 复杂消息:
- 代号:先转化成数字,再根据数字表示转化为原字符。
- 12位散列值:转化为数字,根据哈希判断是否存在对应字符串。
- 收发标志:决定发送方和接收方。
- 将代号转化为12位和25位散列值(先计算字符的数字表示,再计算其散列值),存入哈希表(这一步要最后做)。
- 简单消息
- 接收方代号:
- 大于2的25次方,先减去2的25次方,再根据短数字表示计算原字符串(短字符很坑,需要特殊考虑“00AXX”的情况,因为第一位为0表示空格,第二位为0表示“0”,后几位同理)
- 小于2的25次方,转化为数字,根据哈希判断是否存在对应字符串。
- 将代号转化为12位和25位散列值,存入哈希表(这一步要先做)。
- 发送方代号:同上。
- 位置编号:转化为数字,注意为0的时候不需要输出。
- 接收方代号:
相关函数
qmi:快速幂,计算a的b次方,直接循环计算应该也可以。
change:将01字符串转化为整数。
sh:short短数字表示,将数字转化为字符串。
ha:计算散列值。
sanlie:根据散列值进行查询。
num:计算出字符的数字表示。
solve0:简单消息处理主函数。
solve11:复杂消息代号处理主函数。
solve12:复杂消息12位散列值处理主函数。
代码
有注释版本(ai注释)
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
int n; // 数据组数
ll c25 = 33554432; // 2^25,用来判断 28 位 01 串是否 >= 2^25
string dat; // 每次读入的一整行字符串
unordered_map<ll,string> mp12, mp25; // 哈希表:key 为 12/25 位哈希值,value 为压缩后的串
/* 快速幂:base^m,用来算 38^k */
ll qmi(ll base,ll m){
ll res = 1;
for(; m; m /= 2, base *= base)
if(m & 1) res *= base;
return res;
}
/* 把 01 串转成十进制整数 */
ll change(string s){
ll res = 0, base = 1;
for(int i = s.size() - 1; i >= 0; i--){
if(s[i] == '1') res += base;
base *= 2;
}
return res;
}
/* 把整数 hash 映射成 3~5 位的“短码”:先字母后数字再混合 */
string sh(ll hash){
string res;
ll op;
/* 3 个大写字母 */
for(int i = 0; i < 3; i++){
op = hash % 26;
res += (char)(op + 'A');
hash /= 26;
}
/* 1 位数字 */
if(hash > 0){
op = hash % 10;
res += (char)(op + '0');
hash /= 10;
}else res += '0';
/* 1 位 0-9/A-Z */
if(hash > 0){
op = hash % 36;
if(op <= 9) res += (char)(op + '0');
else res += (char)(op + 'A' - 10);
hash /= 36;
}else res += '0';
/* 可能再补 1 位 */
if(hash > 0){
op = hash;
if(op != 0){
if(op <= 10) res += (char)(op + '0' - 1);
else res += (char)(op - 11 + 'A');
}
}
reverse(res.begin(), res.end());
return res;
}
/* 把一个整数 hash 再映射到 0~(2^base-1) 的更小哈希值(高位截取)*/
ll ha(ll op, ll base){
return op * 47055833459ULL >> (64 - base);
}
/* 根据哈希值去表里查,找不到返回 "###" */
string sanlie(ll hash, ll base){
if(base == 25){
if(mp25.count(hash)) return "#" + mp25[hash];
else return "###";
}else{
if(mp12.count(hash)) return "#" + mp12[hash];
else return "###";
}
}
/* 把 10 位 38 进制串(含数字、字母、_)再转回整数 */
ll num(string s){
ll res = 0, base = qmi(38, 10);
for(int i = 0; i < s.size(); i++){
if(isdigit(s[i])) res += base * (s[i] - '0' + 1);
else if(s[i] == '_') res += base * 37;
else res += base * (s[i] - 'A' + 11);
base /= 38;
}
return res;
}
/* 28 位 01 串 -> 压缩串 */
string solve0(string s){
ll hash = change(s);
if(hash >= c25){ // >= 2^25 走 11 位
hash -= c25;
string s1 = sh(hash); // 先映射成 3~5 位短码
hash = num(s1); // 再转成 38 进制整数
mp12[ha(hash, 12)] = s1; // 填两张哈希表
mp25[ha(hash, 25)] = s1;
return s1;
}else{
return sanlie(hash, 25); // 直接查 25 位哈希
}
}
/* 58 位 01 串 -> 11 位 38 进制串(带 _) */
string solve11(string s){
ll x = change(s), op;
string res;
for(int i = 0; i < 11; i++){
op = x % 38;
if(op != 0){
if(op <= 10) res += (char)(op + '0' - 1);
else if(op <= 36) res += (char)(op + 'A' - 11);
else res += '_';
}
x /= 38;
}
reverse(res.begin(), res.end());
ll hash = num(res); // 再算一次 38 进制整数
mp12[ha(hash, 12)] = res; // 填两张哈希表
mp25[ha(hash, 25)] = res;
return res;
}
/* 12 位 01 串 -> 查表返回 */
string solve12(string s){
ll hash = change(s);
if(mp12.count(hash)) return '#' + mp12[hash];
else return "###";
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> dat;
string s1, s2, s;
if(dat[0] == '0'){ // 类型 0
s1 = solve0(dat.substr(1, 28)); // 前 28 位
s2 = solve0(dat.substr(29, 28)); // 中 28 位
ll pos = change(dat.substr(57, 15)); // 后 15 位
if(pos != 0) cout << s1 << ' ' << s2 << ' ' << pos << endl;
else cout << s1 << ' ' << s2 << endl;
}else{ // 类型 1
s2 = solve12(dat.substr(59, 12)); // 后 12 位
s1 = solve11(dat.substr(1, 58)); // 前 58 位
if(dat[71] == '0') swap(s1, s2); // 最后一位决定顺序
cout << s1 << ' ' << s2 << endl;
}
}
}
无注释版本
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
int n;
ll c25=33554432;
string dat;
unordered_map<ll,string>mp12,mp25;
ll qmi(ll base,ll m){
ll res=1;
for(;m;m/=2,base*=base)
if(m&1)res*=base;
return res;
}
ll change(string s){
ll res=0,base=1;
for(int i=s.size()-1;i>=0;i--){
if(s[i]=='1')res+=base;
base*=2;
}
return res;
}
string sh(ll hash){
string res;
ll op;
for(int i=0;i<3;i++){
op=hash%26;
res+=(char)(op+'A');
hash/=26;
}
if(hash>0){
op=hash%10;
res+=(char)(op+'0');
hash/=10;
}else res+='0';
if(hash>0){
op=hash%36;
if(op<=9)res+=(char)(op+'0');
else res+=(char)(op+'A'-10);
hash/=36;
}else res+='0';
if(hash>0){
op=hash;
if(op!=0)
if(op<=10)res+=(char)(op+'0'-1);
else res+=(char)(op-11+'A');
}
reverse(res.begin(),res.end());
return res;
}
ll ha(ll op,ll base){
return op*47055833459>>(64-base);
}
string sanlie(ll hash,ll base){
if(base==25){
if(mp25.count(hash))return "#"+mp25[hash];
else return "###";
}else{
if(mp12.count(hash))return "#"+mp12[hash];
else return "###";
}
}
ll num(string s){
ll res=0,base=qmi(38,10);
int cnt=0;
for(int i=0;i<s.size();i++){
if(isdigit(s[i]))res+=base*(s[i]-'0'+1);
else if(s[i]=='_')res+=base*37;
else res+=base*(s[i]-'A'+11);
base/=38;
}
return res;
}
string solve0(string s){
ll hash=change(s);
if(hash>=c25){
hash-=c25;
string s1=sh(hash);
hash=num(s1);
mp12[ha(hash,12)]=s1;
mp25[ha(hash,25)]=s1;
return s1;
}else{
return sanlie(hash,25);
}
}
string solve11(string s){
ll x=change(s),op;
string res;
for(int i=0;i<11;i++){
op=x%38;
if(op!=0){
if(op<=10)res+=(char)(op+'0'-1);
else if(op<=36)res+=(char)(op+'A'-11);
else res+='_';
}
x/=38;
}
reverse(res.begin(),res.end());
ll hash=num(res);
mp12[ha(hash,12)]=res;
mp25[ha(hash,25)]=res;
return res;
}
string solve12(string s){
ll hash=change(s);
if(mp12.count(hash))return '#'+mp12[hash];
else return "###";
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>dat;
string s1,s2,s;
if(dat[0]=='0'){
s1=solve0(dat.substr(1,28));
s2=solve0(dat.substr(29,28));
ll pos=change(dat.substr(57,15));
if(pos!=0)cout<<s1<<' '<<s2<<' '<<pos<<endl;
else cout<<s1<<' '<<s2<<endl;
}else{
s2=solve12(dat.substr(59,12));
s1=solve11(dat.substr(1,58));
if(dat[71]=='0')swap(s1,s2);
cout<<s1<<' '<<s2<<endl;
}
}
}