洛谷P1079 Vigenère 密码题解

文章介绍了使用ASCII码进行加密和解密的方法,重点在于如何通过字母在ASCII码表中的位置进行操作。解密函数`r(letter_k,letter_c)`通过移动字母表并找到对应位置来还原原始字符。此外,还讨论了密钥处理和join函数的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这道题已经不接受题解了,但我还是要发一篇 ¯\(ツ)

原题链接:P1079https://www.luogu.com.cn/problem/P1079

提交记录:提交·

Ascii码是关键点,有一点要记住:ord(string)将字符转为Ascii码,chr(inter)将Ascii码转为字符。

Ascii码对照表:C语言中文网http://c.biancheng.net/c/ascii/

一大难点就是如何计算R,下面是R的计算表:

 直接把表输入到程序里有些不科学,所以我们来找找规律。

可以看出,对于最左侧的一列字母,每一个字母对应的一行就是把这个字母之前的部分移到这一行的尾端。

f听不懂没关系,我们来看图:

如果第一个参数是D就可以看到D这一行中 D之前的ABC都被移到了尾端,对于别的也是一样,这样我们就可以通过R的一个参数来获取那一行的信息了。下面我们通过第二参数来获取确切的结果。我们先获取第二个参数在字母表中的编号,方法是将他的Ascii值减去64(仅用于大写字母),如果第二个参数是C那这个数字就是3 然后这一列中的3号的元素就是结果啦,如图所示:

 这就是R的计算方法啦!可这是根据两个参数得出结果的方法,即加密的方法,但我们需要的是解密的方法,即R的逆运算。方法就是在得出的列表中找到结果对应的数字,然后转化成Ascii码,就可以得到另一个参数了。上代码(语言:Python):

def r(letter_k,letter_c): #通过密文和密钥进行解密的函数
    #分别是已知的参数(密钥k的字符)和结果(密文C的字符)
    letter_k = letter_k.upper()#只能计算大写字母,所有要转换一下
    letter_c = letter_c.upper()
    letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',  #定义字母表
        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 
        'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    a_num = ord(letter_k)-64 #获取letter_k的Ascii码

    temp = letters[:a_num] #将letter_k之前的部分储存到temp中
    letters = letters[a_num:] #将这部分截掉
    letters = letters+temp #然后移到尾部

    for i in range(len(letters)): #历遍经过处理的字母表
        if letters[i] == letter_c: #如果找到了结果的位置:
            output = chr(i+66) #将其转化为Ascii码(为什么加66而不是64我也不是很清楚)
            if output == '[': #出于某些玄学问题,'A'会变成'[',不过加一个特判就好了
                output = 'A'
            return output #返回结果(明文M的字符,大写)

另一个要注意的是密钥的格式,如果密钥小于或等于密文,那就将密文的长度除以密钥的长度并向上取整(为什么向上取整请参考小学数学练习册)。如果密钥大于密文,就将多余的部分截掉。题目并没有说明这一点,但亲测正确。

完整代码:

import math #为了向上取整函数

def r(letter_k,letter_c): #通过密文和密钥进行解密的函数
    #分别是已知的参数(密钥k的字符)和结果(密文C的字符)
    letter_k = letter_k.upper()#只能计算大写字母,所有要转换一下
    letter_c = letter_c.upper()
    letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',  #定义字母表
        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 
        'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    a_num = ord(letter_k)-64 #获取letter_k的Ascii码

    temp = letters[:a_num] #将letter_k之前的部分储存到temp中
    letters = letters[a_num:] #将这部分截掉
    letters = letters+temp #然后移到尾部

    for i in range(len(letters)): #历遍经过处理的字母表
        if letters[i] == letter_c: #如果找到了结果的位置:
            output = chr(i+66) #将其转化为Ascii码(为什么加66而不是64我也不是很清楚)
            if output == '[': #出于某些玄学问题,'A'会变成'[',不过加一个特判就好了
                output = 'A'
            return output #返回结果(明文M的字符,大写)

k = input() #读取输入
c = input()
m = []

if len(k) <= len(c): #对密钥进行处理,详见正文
    n = math.ceil(len(c)/len(k))
    k = k*n
else:
    k = k[:len(c)]


for i in range(len(c)): #历遍密文
    letter = r(k[i],c[i]) #解密密文
    if ord(c[i]) >= 97 and ord(c[i]) <= 122: #小写字母的Ascii码是97~122
        #如果密文是小写,明文也转换为小写
        letter = letter.lower()
    m.append(letter)

print(''.join(m)) #join将作为参数的列表中的元素用字符串连接起来,详解正文

最后再讲一下join函数,join属于string类型,将作为参数的列表中的元素用字符串连接起来,比如:

'_'.join(['a','b','c'])

会返回一个字符串"a_b_c",如果是

"".join(a_list)

会将列表直接转换为字符串,非常实用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值