&运算符的用法

本文介绍了Vigenère密码的加密原理,并展示了如何利用按位与运算符"&"简化解密算法,提供了一个简化的C++代码实现,通过取字母的二进制与31进行位运算来确定其在字母表中的位置。

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

按位与运算符"&"是双目运算符是参与运算的两数各对应的二进位相与。

按位与"&"功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。

例如:9&5可写算式如下: 00001001 (9的二进制补码)&00000101 (5的二进制补码)

00001001

00000101

对比2个数的补码可以得出结构为 00000001 也就是1的二进制补码

可见9&5=1。 按位与运算通常用来对某些位清0或保留某些位。

当然通过一道题 我发现了 &与字母顺序有关的题 有着十分便利的妙用。

题目描述

16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法 Vigenère 密码。Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为南军所广泛使用。

在密码学中,我们称需要加密的信息为明文,用 MM 表示;称加密后的信息为密文,用 CC 表示;而密钥是一种参数,是将明文转换为密文或将密文转换为明文的算法中输入的数据,记为 kk。 在 Vigenère 密码中,密钥 kk 是一个字母串,k=k_1,k_2,…,k_nk=k1​,k2​,…,kn​。当明文 M=m_1,m_2,…,m_nM=m1​,m2​,…,mn​ 时,得到的密文 C=c_1,c_2,…,c_nC=c1​,c2​,…,cn​,其中 c_ici​=m_imi​®k_iki​,运算®的规则如下表所示:

Vigenère 加密在操作时需要注意:

  1. ®运算忽略参与运算的字母的大小写,并保持字母在明文 MM 中的大小写形式;

  2. 当明文 MM 的长度大于密钥 kk 的长度时,将密钥 kk 重复使用。

例如,明文 M=Helloworld,密钥k=abc 时,密文 C=Hfnlpyosnd。

输入格式

共 2 行。

第一行为一个字符串,表示密钥 kk,长度不超过 100,其中仅包含大小写字母。

第二行为一个字符串,表示经加密后的密文,长度不超过 1000,其中仅包含大小写字母。

输出格式

一个字符串,表示输入密钥和密文所对应的明文。

输入输出样例

输入 #1复制

CompleteVictory
Yvqgpxaimmklongnzfwpvxmniytm

输出 #1复制

Wherethereisawillthereisaway

按照题目所说也好理解 只需要先弄出秘钥字母的位子 再用密文给的字母减去秘钥的位子就可以得到明文。

那么首先我们可以先需要3个数组存放密文 秘钥 明文;

然后测出密文和秘钥的长度 ,由于秘钥基本都比密文短 还需要拼接,
cin>>k>>s;
len1 = strlen(k);//测出密钥长度
len2 = strlen(s);//密文长度
if (len1 < len2)
    for (int i = len1; i < len2; i++)    k[i] = k[i - len1];

来看核心部分

首先是大小写问题 特判即可,再者明文是密文的字母减去秘钥字母得到的。所以会出现减到a一下的情况需要额外分类。只需要加上26就行了。

for (int i = len1; i < len2; i++)    k[i] = k[i - len1]; for (int i = 0, j; i < len2; i++)
    {
        if (k[i] >= 'A' && k[i] <= 'Z')    j = k[i] - 'A';
        if (k[i] >= 'a' && k[i] <= 'z')    j = k[i] - 'a';
        ans[i] = s[i] - j;
        if (s[i] >= 'A' && s[i] <= 'Z')
            if (ans[i] < 'A')    ans[i] += 26;
        if (s[i] >= 'a' && s[i] <= 'z')
            if (ans[i] < 'a')    ans[i] += 26;
    }

完整的代码是

#include<bits/stdc++.h>
char k[1010], s[1010], ans[1010];
int len1, len2;
int main()
{
    cin >> k >> s;
    len1 = strlen(k);//测出密钥长度
    len2 = strlen(s);//密文长度
    if (len1 < len2)
        for (int i = len1; i < len2; i++)    k[i] = k[i - len1];
    for (int i = 0, j; i < len2; i++)
    {
        if (k[i] >= 'A' && k[i] <= 'Z')    j = k[i] - 'A';
        if (k[i] >= 'a' && k[i] <= 'z')    j = k[i] - 'a';
        ans[i] = s[i] - j;
        if (s[i] >= 'A' && s[i] <= 'Z')
            if (ans[i] < 'A')    ans[i] += 26;
        if (s[i] >= 'a' && s[i] <= 'z')
            if (ans[i] < 'a')    ans[i] += 26;
    }
    for (int i = 0; i < len2; i++)    printf("%c", ans[i]);/
    return 0;
}

是不是很长。我去看了其他大佬的解题方法,发现了一种十分简单的代码 及时使用了&的运算

再发简易之前我们先来看点东西

0100 00016541A
0100 00106642B
0100 00116743C
0100 01006844D
0100 01016945E
0100 01107046F
0100 01117147G
0100 10007248H
0100 10017349I
0100 1010744AJ
0100 1011754BK
0100 1100764CL
0100 1101774DM
0100 1110784EN
0100 1111794FO
0101 00008050P
0101 00018151Q
0101 00108252R
0101 00118353S
0101 01008454T
0101 01018555U
0101 01108656V
0101 01118757W
0101 10008858X
0101 10018959Y
0101 1010905AZ
0110 00019761a
0110 00109862b
0110 00119963c
0110 010010064d
0110 010110165e
0110 011010266f
0110 011110367g
0110 100010468h
0110 100110569i
0110 10101066Aj
0110 10111076Bk
0110 11001086Cl
0110 11011096Dm
0110 11101106En
0110 11111116Fo
0111 000011270p
0111 000111371q
0111 001011472r
0111 001111573s
0111 010011674t
0111 010111775u
0111 011011876v
0111 011111977w
0111 100012078x
0111 100112179y
0111 10101227Az

直接看对应的字母a的二进制代码      A的二进制码

0110 0001                                     0100 0001

x 跟X

0111 1000            0101 1000

可以看到 相同字母的后5位二进制是相同的。

而且用对应字母&31(11111)得到的就是改字母在字母表中的顺序

例如a&31  

0110 0001

0001 1111 

0000 0001

也就是a&31=1(0000 0001)

大写的由于后5位相同也是同理

利用这个性质

我们就只需要考虑边界值问题即可

#include<bits/stdc++.h>
using namespace std;
int main() {
    string k, c;
    cin >> k >> c;
    for (int i = 0; i < c.length(); i++) {
        int t = (k[i % k.length()] & 31) - 1;
        c[i] = (c[i] & 31) - t > 0 ? c[i] - t : c[i] - t + 26;
    }
    cout << c << endl;
    return 0;
}

叶jq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值