题目描述
来源
OpenJudge网站 —— 百练习题集-第4112号习题
要求
总时间限制: 3000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB
描述
A国和B国正在进行一场战争。A国通过间谍知道B国的情报加密规则为:
-
仅对字母加密,其他符号保留(如空格,逗号等)
-
对第i个单词(i从1开始)的加密方法是把第i个单词反转(如abc变成cba),然后对单词内的每个字母采用经典的Caesar加密法,循环后移i个字母。例如:第5个单词的加密表如下所示:
密码字母:A B C D E F G H I J K L M N O P Q R S T U VW X Y Z
原文字母:V W X Y Z A B C D E F G H I J K L M N O P QR S T U
-
单词的定义是:任何一串极大的连续字母串。
比如,样例输出第三行,第二个单词为ba,反转后为ab,a后移2个单词变成c,b后移两个单词变成d,故该单词加密后为cd(见样例输入第三行)。
现在A国又截获了一些B国的情报密文。请你帮A国破译出情报的内容。
输入
一共有不超过int范围行,每行为一个字符串(int范围)。注意,每行是可以以空格开头的。
输出
情报破译后得到的内容。每条情报对应输出一行。
样例输入
fiU umncv oolz ioex jhfqu, zg uh zqI nlaxO ockl yz kmpzgE.
fX gxcj , ghlsxffr cxmG K.
ab3cd
样例输出
The talks will take place, at an Air Force base on Sunday.
We have , occupied City F.
az3ba
解题思路
- 问题是求一行密文对应的明文。下面的代码中,crack函数封装了破译密文的逻辑。
- 做法是,从左到右地,对每一个单词进行破译(crack_word函数),而非字母无需改变,输出破译得到的明文。
- 如何得出一个个单词?做法是,从左到右扫描,遇到一个字母,则找出该位置打头的连续的字母,直至遇到非字母或者字符串结束。下面的代码中,find_word函数封装了找单词的逻辑。
- 已知一个作为密文的单词cword,凯撒秘钥是key, 如何得到其明文pword?做法是,cword逆序得到rev_cword,接着对于rev_cword内的每一个字母s,得到其加密前的字母r。这些字母r连在一起,就是明文单词word。
- 已知密文字母s,凯撒秘钥是key,如何求出其对应的明文字母r?做法是,先判别s字母的大小写,接着转换为明文字母。无论是大写字母还是小写字母,做法类似。如果是大写,则用字母s的编码值减去key,如果减出来的结果小于’A’的编码值,则加上26,求出来的结果作为编码转换为字母,该字母即为明文字母。
参考答案
import sys
#得到密文miwen对应的明文
def crack(miwen):
mingwen = ''
index = 0
word_count = 0
while index < len(miwen):
if miwen[index].isalpha():
cword = find_word(miwen, index)
word_count += 1
pword = crack_word(cword, word_count)
mingwen += pword
index += len(cword)
else:
mingwen += miwen[index]
index += 1
return mingwen
#在line字符串内,找到从index索引位置开始的单词。
#line[index]是字母
def find_word(line, index):
word = ''
while index < len(line) and line[index].isalpha():
word += line[index]
index += 1
return word
#对单词进行解密,凯撒秘钥是key
def crack_word(cword, key):
rev_cword = cword[::-1]
pword = ''
for s in rev_cword:
pword += casear_crack(s, key)
return pword
#对字母进行凯撒解密,秘钥是key
def casear_crack(s, key):
if s.isupper():
base_letter = 'A'
else:
base_letter = 'a'
dif = ord(s) - ord(base_letter) - key
if dif < 0:
dif += 26
p = chr(dif + ord(base_letter))
return p
for line in sys.stdin: #读入行数不定的文本
print(crack(line), end='') #键盘输入时,按Ctrl + Z或Ctrl + D来表明结束输入
测试用例
- 题目给出的测试用例覆盖了以下情形:(1)输入多行文本。(2)key=1, 2, 3, 4乃至更大。(3)包含大小写字母。(4)包含非字母字符。(5)单词之间用数字分隔。差不多够用了。
- 输入一行文本。
样例输入
cba
样例输出
zab
小结
- 自顶向下逐步细化。细化的步骤有复杂度的话,用函数封装实现。