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;
}
相关推荐
xiaoye-duck2 天前
《算法题讲解指南:优选算法-字符串》--61.最长公共前缀,62.最长回文子串,63.二进制求和,64.字符串相乘
c++·算法·字符串
itman3014 天前
C语言字符串必知:末尾有个隐藏的\0,新手易踩坑
c语言·字符串·内存管理·库函数·指针操作
汉克老师5 天前
GESP2024年12月认证C++三级( 第一部分选择题(9-15))
c++·字符串·位运算·进制·补码·gesp三级·gesp3级
汉克老师5 天前
GESP2024年12月认证C++三级( 第一部分选择题(1-8))
c++·字符串·二进制·八进制·补码·gesp三级·gesp3级
王老师青少年编程6 天前
csp信奥赛c++之字符数组与字符串的区别
c++·字符串·字符数组·csp·信奥赛
Tisfy8 天前
LeetCode 3474.字典序最小的生成字符串:暴力填充
算法·leetcode·字符串·题解
汉克老师9 天前
GESP2025年6月认证C++三级( 第一部分选择题(9-15))
c++·字符串·位运算·gesp三级·gesp3级
Tisfy11 天前
LeetCode 2839.判断通过操作能否让字符串相等 I:if-else(两两判断)
算法·leetcode·字符串·题解
Thomas.Sir13 天前
第三章:Python3 之 字符串
开发语言·python·字符串·string
Hknll17 天前
CSP第33次认证题解
数据结构·c++·算法·stl·字符串·csp认证·vector/array