题目描述
古罗马帝国有一个拥有各种部门的强大政府组织。其中一个部门就是保密服务部门。为了保险起见,在省与省之间传递的重要文件中的大写字母是加密的。当时最流行的加密方法是替换和重新排列。
替换方法是将所有出现的字符按照一个规则替换,比如 ABCDEFGHIJKLMNOPQRSTUVWXYZ 到 BCDEFGHIJKLMNOPQRSTUVWXYZA,如果原词是 VICTORIOUS
则它变成 WJDUPSJPVT
。
排列方法改变原来单词中字母的顺序。例如:将顺序 (left<2,1,5,4,3,7,6,10,9,8
ight>) 应用到 VICTORIOUS
上,则得到 IVOTCIRSUO
。
人们很快意识到单独应用替换方法或排列方法加密,都是很不保险的。但是如果结合这两种方法,在当时就可以得到非常可靠的加密方法。所以,很多重要信息先使用替换方法加密,再将加密的结果用排列的方法加密。用两种方法结合就可以将 VICTORIOUS
加密成 JWPUDJSTVP
。
考古学家最近在一个石台上发现了一些信息。初看起来它们毫无意义,所以有人设想它们可能是用替换和排列的方法被加密了。人们试着解读了石台上的密码,现在他们想检查解读的是否正确。他们需要一个计算机程序来验证,你的任务就是写这个验证程序。
输入格式
输入有两行。
第一行是石台上的文字 (S)。文字中没有空格,并且只有大写英文字母。
第二行是被解读出来的加密前的文字 (T)。第二行也是由大写英文字母构成的。
输出格式
如果第二行经过某种加密方法后可以产生第一行的信息,输出 YES
,否则输出 NO
。
数据范围
测试时间限制 (1000 mathrm{ms}),空间限制 (512 mathrm{MiB})
- 对于 (30\%) 的数据,(|S|,|T|le 10);
- 对于 (50\%) 的数据,(|S|,|T|le 50);
- 对于 (100\%) 的数据,(|S|,|T|le 100)。
分析
这道题的关键就是要抓住不变量来解决问题。
注:设 (n) 与 (|S|),(|T|) 同阶。
(30 mathtt{pts})
很简单,就是枚举 (26) 种情况,然后枚举所有排列,看看有没有重合的。
复杂度:(Theta(n imes n!))
(100 mathtt{pts})
这也很简单,只要枚举 (26) 种情况,分别排序并核对即可。
复杂度:(Theta(n^2log n))
(mathcal{E}xtra mathcal{S}core)
如果此题的数据范围提升到 (nle 10^5),还有没有方法做呢?
答案是有的。关键是我们要学会抓住不变量。
注意到两个方法会对原串修改,但是这个修改是有迹可循的。
比如说,字符排列的不变量就是字符集合(这里的集合是多重集合,也就是说可以有重复元素)。
同时,按照规则替换不变的就是固定字符出现的数量。
比如,ABCDDEEE
无论怎么改,必然有一个字符出现三次,有一个出现两次,而还有三个字符各出现一次。
注意到,字符集合不变也意味着固定字符出现的数量不变。
那么,我们能不能就此判断固定字符出现次数不变就是评判标准呢?
NoNoNo,我们还要证明,对于所有的固定字符出现次数不变的情况,必然会存在一种操作方法,使得这两个字符串可以互相转化。
其实很简单。我们只要证明存在一个字符串 (A),使得 (S) 可以通过交换法则得到 (A),且 (A) 的字符集与 (T) 对应。
因为固定字符出现次数不变,我们只要对其出现次数排序则两个数列必然相同。让对应的字符转换即可。
这样,我们就能够在 (Theta(n+|U|log |U|)) 内完成问题,其中 (U) 是字符集。
Code
这里的写法用的是最后一种方法,还是比较快的。
#include <cstdio>
#include <algorithm>
using namespace std;
const int max_n = 100, max_charset = 26;
char s1[max_n+1], s2[max_n+1];
int cnt1[max_charset] = {}, cnt2[max_charset] = {};
int main()
{
gets(s1);
gets(s2);
for (int i = 0; s1[i]; i++)
cnt1[s1[i]-'A']++;
for (int i = 0; s2[i]; i++)
cnt2[s2[i]-'A']++;
sort(cnt1, cnt1 + max_charset);
sort(cnt2, cnt2 + max_charset);
for (int i = 0; i < max_charset; i++)
if (cnt1[i] != cnt2[i])
{
puts("NO");
return 0;
}
puts("YES");
return 0;
}
后记
这道题很水,但是我们可以试图找到更有意思的做法来增加难度。
同时,我们也要在不确定时用证明来给出答案。