题目
最后输出字符最多m个(m<=1e5),有n(n<=256)个加密条文,
第i个条文由一个ASCII值Ci,对应一段01字符串Si(|Si|<=10),
任一个字符串都不会是另一个的前缀
以下输入一个16进制的data串(|data|<=2e5)(含大小写字母A/a-F/f)
将data九位九位地数,去掉最后剩下的余数位;
每九位的第九位为校验位,对前八位进行奇校验
若这九位的1的个数为奇数,则保留前八位,否则舍弃;
对于保留下的01串,将其按加密条文对应表解密,并取其前m位输出
思路来源
https://blog.youkuaiyun.com/Originum/article/details/82530717
题解
凯神的题解还是写得很详细鸭,让自己更好地模拟了一下题意
校验看似复杂,实则不到10行就能搞过去
map那里其实不怎么会写简易的版本,就生生映射了一下
匹配时,在01字典树上匹配,成功匹配后就返回根rt
心得
终于能手敲01字典树的数组版本了
感觉过了几个月之后,自己对于某些知识的理解终于加深了
似乎熟练掌握一个领域的知识,至少在学习这个知识3个月后,还是菜啊.jpg
所幸自己上个学期开了挺多新知识的
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2600;
const int maxm=1e6+10;
int t,m,n,rt;
int tr[maxn*2][2],cnt;
int num[maxn*2],len;
int tot,v;
string s,ans,res;
char ch[260];
map<char,string>to;
void init()
{
to['0']="0000";
to['1']="0001";
to['2']="0010";
to['3']="0011";
to['4']="0100";
to['5']="0101";
to['6']="0110";
to['7']="0111";
to['8']="1000";
to['9']="1001";
to['A']="1010";
to['B']="1011";
to['C']="1100";
to['D']="1101";
to['E']="1110";
to['F']="1111";
to['a']="1010";
to['b']="1011";
to['c']="1100";
to['d']="1101";
to['e']="1110";
to['f']="1111";
}
void add(int rt,string s,int id)
{
int len=s.size();
for(int i=0;i<len;++i)
{
int v=s[i]-'0';
if(!tr[rt][v])
tr[rt][v]=++cnt;
rt=tr[rt][v];
}
num[rt]=id;
}
void find(string s)
{
int rt=0;
int len=s.size();
for(int i=0;i<len;++i)
{
int v=s[i]-'0';
rt=tr[rt][v];
if(num[rt])
{
putchar(ch[num[rt]]);
m--;
if(!m)break;//最多输出m个字符
rt=0;
}
}
puts("");
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
init();
cin>>t;
while(t--)
{
rt=cnt=0;
memset(tr,0,sizeof tr);
memset(num,0,sizeof num);
cin>>m>>n;
for(int i=1;i<=n;++i)
{
cin>>v>>s;
ch[i]=v;
add(rt,s,i);
}
cin>>s;
len=s.size();
res.clear();
for(int i=0;i<len;++i)
res+=to[s[i]];
len=res.size();
ans.clear();
for(int i=0;i<len/9*9;i+=9)
{
tot=0;
for(int j=0;j<=8;++j)
if(res[i+j]=='1')tot++;
if(tot%2==0)continue;//奇校验
for(int j=0;j<=7;++j)
ans+=res[i+j];
}
//cout<<ans<<endl;
find(ans);
}
return 0;
}