凯撒密码
[Description]
Gemini最近喜欢上了历史,他了解到历史上有一种神奇的加密方法叫做凯撒密码。
凯撒密码非常的简单,就是把每个字母向后移动m位(z的后一位是a)。例如,当m=1,abcd加密后就是bcde,当m = 5,xyz加密后会变成cde。
Gemini对学会一种加密方法表示非常兴奋,于是,他构造了大量长度为5的纯英文小写密文(为什么是5?我也不知道)。然后……,然后他把哪个明文对应哪个密文搞混了。(-_-|||)
幸运的是,经过分析,还是可以找出明文和密文的对应关系的如“dtfeg”,“tiyoj”,“eugfh”,
“vkaql”四个串,明显“dtfeg”,“eugfh”相互对应(m =1),“tiyoj”,“vkaql”相互对应(m = 2)。
然而,看到海量的字符串,Gemini失去了手动匹配的信心,于是他机智地拿出了Raspberry Pi,开始写程序。
[Input]
第一行,一个整数N,表示有N段明文,N段密文。
第二行,N个长度为5的字符串,表示N段密文。
第三行,N个长度为5的字符串,表示N段明文。
[Output]
N行,每行两个整数ai,mi。ai表示第i 段明文对应的密文编号(从1开始),mi表示从第
i 段明文加密得到第i段密文所用的密钥m。
[Sample]
Sample Input
2
eugfh vkaql
dtfeg tiyoj
Sample Output
1 1
2 2
[Hint]
2 ≤ N ≤ 300 000。
保证有且只有一组解。
m的取值范围为[0,25]
一道神tm坑爹的哈希!我试图用普通的字符串哈希,然后强行枚举每个串mi从0到5的情况,然后boom超时一半。 显然题解用了一种更高明的方法,因为明文和密文的对应关系是唯一的,所以前后两个字符的ASCII码的差值也就固定了(如果有重复的那就混乱了,一个明文可能和多个密文对应,一个密文也可以和多个明文对应)。 所以设计一个26进制的哈希,第i位代表第i个字母和i+1个字母的ASCII码差值,这样每一个串就有唯一的哈希值了。然后进行普通的哈希处理即可。
program mys;
type
node=record
data:string[5];
num:longint;
end;
var a:string;
b:string[5];
st:ansistring;
ch:char;
i,j,k,m,n,t,ha:longint;
s:array[0..300000]of string[5];
p:array[0..600000]of node;
function hash(x:string):longint;
var i,y:longint;
begin
hash:=0;
for i:=1 to 4 do
begin
y:=ord(x[i+1])-ord(x[i]);
if y<0 then y:=y+26;
hash:=hash*26;
hash:=hash+y;
end;
end;
procedure make(x:string;z:longint);
var ha:longint;
begin
ha:=hash(x);
p[ha].num:=z;
p[ha].data:=x;
end;
procedure find(x:string);
var
y,h:longint;
ss:string[5];
begin
h:=hash(x);
ss:=p[h].data;
y:=ord(ss[1])-ord(x[1]);
if y<0 then y:=y+26;
writeln(p[h].num,' ',y);
end;
begin
assign(input,'caesar.in'); reset(input);
assign(output,'caesar.out'); rewrite(output);
readln(n);
for i:=1 to n do
begin
b:='';
read(ch);
while (ch>='a')and(ch<='z') do
begin
b:=b+ch;
read(ch);
end;
make(b,i);
end;
readln;
for i:=1 to n do
begin
read(ch);
while (ch>='a')and(ch<='z') do
begin
s[i]:=s[i]+ch;
read(ch);
end;
end;
for i:=1 to n do
find(s[i]);
close(input);
close(output);
end.