3405. W的密码(北京大学考研机试题)

3405. W的密码

⭐️难度:简单(其实超级困难)

⭐️类型:字符串

📖题目:题目链接

Weird Wally 的无线部件公司(简称 W公司)能够生产各种各样的小型无线网络设备,从狗项圈到铅笔,再到钓鱼浮筒,一应俱全。这些小型设备的内存空间都十分有限,因此,像 Rijndael 这样的高级加密标准的加密算法虽然足够安全,但是并不适用。为了保障设备之间的信息传输有足够的安全性,W公司将使用以下加密算法,该算法需要你来实现。

加密消息需要三个整数密钥,k1、k2和 k3。

将字母 a∼i分为第一组,

字母 j∼r分为第二组,

字母 s∼z与下划线 _ 分为第三组。

在对消息进行加密时,要将消息中涉及到的每个组的字符,在组内进行向左旋转 ki 个位置(位于组内相对位置最左端的字符向左移动一位会到组内相对最右端的位置)。

每个组之间都是相互独立的,也就是说在旋转操作完成后,每个组的字符所占据的位置集合不会发生变化,只是组内各字符的相对位置可能发生变化。

解密即每个组内进行向右旋转 ki 位置。

例如,在 ki 值分别为 2,3,1时,对消息 the_quick_brown_fox 进行加密,我们可以得到加密后的字符串 _icuo_bfnwhoq_kxert。

下图显示了每个组中一个字符的解密右旋转。

观察加密后字符串中包含的全部第 1组(a∼i)的字符,可发现有 {i,c,b,f,h,e},分别位于位置 {2,3,7,8,11,17}。

在向右旋转 k1=2个位置后,字符的相对位置变为了 {h,e,i,c,b,f}。

下表显示了加密字符串依次完成第一组旋转解密、第二组旋转解密和第三组旋转解密后的具体字符串表示。

注意,在对其中一组字符进行旋转时,不会影响到其他组的字符。

现在,给定加密字符串和 k1,k2,k3的值,请你求出解密后的字符串。


输入样例:

2 3 1

_icuo_bfnwhoq_kxert

1 1 1

bcalmkyzx

3 7 4

wcb_mxfep_dorul_eov_qtkrhe_ozany_dgtoh_u_eji

2 4 3

cjvdksaltbmu

0 0 0

输出样例:

the_quick_brown_fox

abcklmxyz

the_quick_brown_fox_jumped_over_the_lazy_dog

ajsbktcludmv

🌟思路:

先理清楚题目,

以题目例子为例,先将字符串分组:

再将第一组单独抽出来进行左旋转2位,

第二组单独抽出来进行左旋转3位:

第三组单独抽出来进行左旋转2位:

最后三组旋转结果整合在一起:

与题目的结果一样。

以上是加密过程,题目要求解密,只需要把左旋转改成右旋转就行。

1️⃣分组

利用三个数组分别存储每个分组中的字符在原字符串中下标,方便后续进行旋转。

2️⃣🌟右旋转

右旋的写法

方案一:(空间复杂度低)

找到旋转边界,左边翻转一次,

右边翻转一次,

整体翻转一次。

方案二:(空间复杂度高)

方案二思路比较直接

🍎代码:

cpp 复制代码
// 难点:旋转
void youxuan(string& str, vector<int> vec, int k) {
    vector<char> tmp;
    if (vec.size() != 0 && k > vec.size()) {
        k = k % vec.size(); // 例如:数组长度为5,右移7位或者右移2位是一样的。
    }

    for (int i = vec.size() - k;i < vec.size();i++) { // 先保存右半部分,避免数据被覆盖
        tmp.push_back(str[ vec[i] ]); // 应存字符
    }

    for (int i = vec.size() - k - 1;i >= 0;i--) { // 旋转左部分
        str[ vec[i + k] ] = str[ vec[i] ];
    }

    for (int i = 0;i < k;i++) { // 旋转右部分
        str[ vec[i] ] = tmp[i];
    }
}

📚题解:

自己写:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<vector>  // vector不需要.h
#include<list>
#include<set>  // // 可以用 set 和 multiset
#include<unordered_set> // 可以用 unordered_set 和 unordered_multiset
#include<map>  // 可以用 map 和 multimap
#include<unordered_map> // 可以用 unordered_map 和 unordered_multimap
#include<algorithm>
#include<string>
#include<iostream>

using namespace std;

// 分组
void fenzu(string& str, vector<int>& vec1,vector<int>& vec2,vector<int>& vec3) {
    for (int i = 0;i < str.size();i++) {
        if (str[i] >= 'a' && str[i] <= 'i') {
            vec1.push_back(i); // 在原字符串中的下标压入数组
        }
        else if (str[i] >= 'j' && str[i] <= 'r') {
            vec2.push_back(i);
        }
        else {
            vec3.push_back(i);
        }
    }
}

// 难点:旋转
void youxuan(string& str, vector<int> vec, int k) {
    vector<char> tmp;
    if (vec.size() != 0 && k > vec.size()) {
        k = k % vec.size(); // 例如:数组长度为5,右移7位或者右移2位是一样的。
    }

    for (int i = vec.size() - k;i < vec.size();i++) { // 先保存右半部分,避免数据被覆盖
        tmp.push_back(str[ vec[i] ]); // 应存字符
    }

    for (int i = vec.size() - k - 1;i >= 0;i--) { // 旋转左部分
        str[ vec[i + k] ] = str[ vec[i] ];
    }

    for (int i = 0;i < k;i++) { // 旋转右部分
        str[ vec[i] ] = tmp[i];
    }
}

int main() {
   
    string str ;
    int k1, k2, k3;

    while (scanf("%d%d%d", &k1, &k2, &k3) != EOF) {
        if (k1 == 0 && k2 == 0 && k3 == 0) {
            break;
        }

        char arr[1000] = { 0 };
        scanf("%s", arr);
        str = arr;

        // 进行分组
        vector<int> vec1;
        vector<int> vec2;
        vector<int> vec3;
        fenzu(str, vec1, vec2, vec3);

        // 进行右旋转
        youxuan(str, vec1, k1);
        youxuan(str, vec2, k2);
        youxuan(str, vec3, k3);

        printf("%s\n", str.c_str());
    }

    return 0;
}

答案:

cpp 复制代码
 // t h e _ q u i c k _ b r o w n _ f o x
// 3 1 1 3 2 3 1 1 2 3 1 2 2 3 2 3 1 2 3
//   h e       i c     b           f    
//   i c       b f     h           e
//         q       k     r o   n     o  
//         o       n     o q   k     r
// t     _   u       _       w   _     x
// _     u   _       w       _   x     t
//


//   i c       b f     h           e
//         o       n     o q   k     r
// _     u   _       w       _   x     t
// _ i c u o _ b f n w h o q _ k x e r t

#include <vector>
#include <algorithm>
#include <stdio.h>
#include <string>
using namespace std;
void Partition(string str, vector<int>& vec1, vector<int>& vec2, vector<int>& vec3) {
    int i;
    for (i = 0; i < str.size(); ++i) {
        if (str[i] >= 'a' && str[i] <= 'i') {
            vec1.push_back(i);
        }
        else if (str[i] >= 'j' && str[i] <= 'r') {
            vec2.push_back(i);
        }
        else {
            vec3.push_back(i);
        }
    }
}
void RightRotate(string& str, vector<int>& vec, int offset) {
    vector<char> tmp;
    if (vec.size() != 0 && offset > vec.size()) {
		offset = offset % vec.size();
	}
    for (int i = vec.size() - offset; i < vec.size(); ++i) {
        tmp.push_back(str[vec[i]]);
    }
    for (int i = vec.size() - offset - 1; i >= 0; --i) {
        str[vec[i + offset]] = str[vec[i]];
    }
    for (int i = 0; i < tmp.size(); ++i) {
        str[vec[i]] = tmp[i];
    }
}
int main() {
    string str;
    int k1, k2, k3;
    while (scanf("%d%d%d", &k1, &k2, &k3) != EOF) {
        if (k1 == 0 && k2 == 0 && k3 == 0) {
            break;
        }
        char arr[1000] = { 0 };
        scanf("%s", arr);
        str = arr;

        vector<int> vec1, vec2, vec3;
        // vec1 存储1分组在str中的下标
        Partition(str, vec1, vec2, vec3);
        RightRotate(str, vec1, k1);
        RightRotate(str, vec2, k2);
        RightRotate(str, vec3, k3);

        printf("%s\n", str.c_str());
    }
    
    return 0;
}
相关推荐
码农幻想梦1 天前
3615. 单词个数统计
字符串
季明洵2 天前
反转字符串、反转字符串II、反转字符串中的单词
java·数据结构·算法·leetcode·字符串
2401_841495642 天前
【Python高级编程】近似串匹配
python·算法·动态规划·字符串·数组·时间复杂度·空间复杂度
源代码•宸5 天前
Redis 攻略(Redis Object)
数据库·redis·后端·缓存·字符串·哈希表·type
拼好饭和她皆失6 天前
字符串题型练习
算法·字符串·哈希算法
gjxDaniel15 天前
A+B问题天堂版
c++·算法·字符串·字符数组
张彦峰ZYF19 天前
QLExpress 字符串能力解析:机制、用法与工程实践
字符串·express·qlexpress规则表达力
SunkingYang21 天前
QT中QStringList如何查找指定字符串,有哪些方式?
qt·字符串·查找·子串·qstringlist
闻缺陷则喜何志丹25 天前
【回文 字符串】3677 统计二进制回文数字的数目|2223
c++·算法·字符串·力扣·回文