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;
}