Leetcode 2325.解密消息

news2024/11/19 7:28:09

给你字符串 key 和 message ,分别表示一个加密密钥和一段加密消息。解密 message 的步骤如下:

  1. 使用 key 中 26 个英文小写字母第一次出现的顺序作为替换表中的字母 顺序 。
  2. 将替换表与普通英文字母表对齐,形成对照表。
  3. 按照对照表 替换 message 中的每个字母。
  4. 空格 ' ' 保持不变。
  • 例如,key = "happy boy"(实际的加密密钥会包含字母表中每个字母 至少一次),据此,可以得到部分对照表('h' -> 'a''a' -> 'b''p' -> 'c''y' -> 'd''b' -> 'e''o' -> 'f')。

返回解密后的消息。

示例 1:

输入:key = "the quick brown fox jumps over the lazy dog", message = "vkbs bs t suepuv"
输出:"this is a secret"
解释:对照表如上图所示。
提取 "the quick brown fox jumps over the lazy dog" 中每个字母的首次出现可以得到替换表。

示例 2:

输入:key = "eljuxhpwnyrdgtqkviszcfmabo", message = "zwx hnfx lqantp mnoeius ycgk vcnjrdb"
输出:"the five boxing wizards jump quickly"
解释:对照表如上图所示。
提取 "eljuxhpwnyrdgtqkviszcfmabo" 中每个字母的首次出现可以得到替换表。

提示:

  • 26 <= key.length <= 2000
  • key 由小写英文字母及 ' ' 组成
  • key 包含英文字母表中每个字符('a' 到 'z'至少一次
  • 1 <= message.length <= 2000
  • message 由小写英文字母和 ' ' 组成

我的答案:

一、信息

1.给我字符串key和message

2.key 加密密匙 message 加密信息

3.解密message步骤

1.将key中的26个英文小写字母

二、步骤

该如何设计步骤呢?

第一步 肯定是接收两个字符串

第二步 建立一个字母表字符串

第三步 对传进来的密文字符串进行遍历并与key和标准字母表进行比较

第四步 进行字符数组的替换遍历完为止

第五步 然后输出字符串

三、分析

问题出现:

问题1.如何进行比较替换呢?

其实不难发现每一个对应的字符串下标都相等我们只需要现在key中找到密文的字母然后再字母表字符串中找到对应的字符然后替换即可。

问题2.字符串之间该如何替换呢?

很简单用C++或C里的函数库即可,JAVA还没学完。

我的答案:

为了实现这个解密算法,我们可以按照以下步骤来解决这个问题。

### 思考过程与分析
1. **创建映射表:**
   遍历密钥字符串(key),对于每个字符,如果它是第一次出现,则将其添加到映射表中。映射表可以是一个数组,其中索引是密文字符,值是对应的明文字符。
   
2. **解密消息:**
   遍历消息字符串(message),对于每个字符,查找映射表并将其替换为相应的字符。空格字符保持不变。

3. **边界与异常处理:**
   一些细节需要注意,例如处理key中的空格字符以及确保映射表是完整的(包含所有26个字母)。

### C语言实现

#include <stdio.h>
#include <stdbool.h>
#include <string.h>

void decrypt(char* key, char* message, char* output) {
    bool used[26] = {0}; // 记录哪些字符已经出现过
    char map[26]; // 映射表
    int idx = 0; // 映射表索引
    
    // 创建映射表
    for(int i = 0; key[i] != '\0'; i++) {
        if(key[i] == ' ') continue; // 跳过空格
        if(!used[key[i] - 'a']) {
            map[idx++] = key[i];
            used[key[i] - 'a'] = true;
        }
    }
    
    // 解密消息
    for(int i = 0; message[i] != '\0'; i++) {
        if(message[i] == ' ') {
            output[i] = ' ';
        } else {
            output[i] = 'a' + (message[i] - 'a' + 26 - idx) % 26;
        }
    }
    output[strlen(message)] = '\0'; // 添加字符串结束标记
}

int main() {
    char key[] = "the quick brown fox jumps over the lazy dog";
    char message[] = "vkbs bs t suepuv";
    char output[2000];
    
    decrypt(key, message, output);
    
    printf("%s\n", output); // 输出:this is a secret
    
    return 0;
}

### C++实现

#include <iostream>
#include <unordered_set>
#include <vector>
using namespace std;

string decrypt(const string& key, const string& message) {
    unordered_set<char> used;
    vector<char> map;
    
    // 创建映射表
    for(char c: key) {
        if(c == ' ') continue;
        if(used.find(c) == used.end()) {
            map.push_back(c);
            used.insert(c);
        }
    }
    
    // 解密消息
    string output(message.length(), ' ');
    for(size_t i = 0; i < message.length(); i++) {
        if(message[i] == ' ') continue;
        output[i] = 'a' + (message[i] - 'a' + 26 - map.size()) % 26;
    }
    return output;
}

int main() {
    string key = "the quick brown fox jumps over the lazy dog";
    string message = "vkbs bs t suepuv";
    
    cout << decrypt(key, message) << endl; // 输出:this is a secret
    
    return 0;
}

### Java实现

import java.util.HashSet;

public class DecryptMessage {
    public static String decrypt(String key, String message) {
        HashSet<Character> used = new HashSet<>();
        StringBuilder map = new StringBuilder();
        
        // 创建映射表
        for(char c: key.toCharArray()) {
            if(c == ' ') continue;
            if(!used.contains(c)) {
                map.append(c);
                used.add(c);
            }
        }
        
        // 解密消息
        StringBuilder output = new StringBuilder(message.length());
        for(char c: message.toCharArray()) {
            if(c == ' ') {
                output.append(' ');
                continue;
            }
            output.append((char) ('a' + (c - 'a' + 26 - map.length()) % 26));
        }
        return output.toString();
    }

    public static void main(String[] args) {
        String key = "the quick brown fox jumps over the lazy dog";
        String message = "vkbs bs t suepuv";
        
        System.out.println(decrypt(key, message)); // 输出:this is a secret
    }
}

### 可能遇到的问题
- 如果key中不包含所有26个字母,映射表将无法构建。
- 确保不要修改原始消息字符串,而是创建一个新的字符串来存储解密后的消息。
- 需要跳过key中的空格字符,并且确保输出字符串中的空格与输入消息字符串中的空格对应。
- 需要处理字符的循环映射,例如,z映射到a。

英雄师傅的答案:

char* decodeMessage(char* key, char* message) {
	char hash[256];
	int i;
	char alph = 'a';
	memset(hash, 0, sizeof(hash));
	for (i = 0; key[i]; ++i) {
		if (key[i] != ' ') {
			if (!hash[key[i]]) {
				hash[key[i]] = alph++;
			}
		}
	}
	for (i = 0; , message[i]; ++i) {
		if (message[i] != ' ') {
			message[i] = hash[message[i]];
		}
	}
	return message;
}

英雄师傅的解决方案很聪明,代码也很简洁。下面是代码的分析以及它是如何工作的。

分析过程

  1. 建立哈希表: 这个解决方案中使用了一个hash数组,用于存储密钥(key)中每个字符的映射。hash数组的索引是字符的ASCII值,而值是映射的字符。例如,如果hash['h']'a',那么在消息(message)中的 'h' 将被替换为 'a'

  2. 初始化映射: 初始化变量alph'a',以便将密钥中的每个唯一字符映射到英文字母表中的字符。对于密钥中的每个字符,如果它不是空格并且尚未映射(即 hash[key[i]] 为0),则映射它并递增 alph

  3. 解密消息: 遍历消息中的每个字符。如果字符不是空格,则将其替换为hash中的映射字符。

  4. 返回消息: 函数最终返回修改后的消息字符串。

Leetcode官方题解:

方法一:模拟
思路与算法

我们根据题目的要求进行模拟即可。

具体地,我们使用一个哈希表存储替换表,随后对字符串 key\textit{key}key 进行遍历。当我们遍历到一个不为空格且未在哈希表中出现的字母时,就将当前字母和 cur\textit{cur}cur 作为键值对加入哈希表中。这里的 cur\textit{cur}cur 即为替换之后的字母,它的初始值为字母 ‘a’\text{`a'}‘a’,当哈希表中每添加一个键值对后,cur\textit{cur}cur 就会变为下一个字母。

在这之后,我们再对字符串 message\textit{message}message 进行遍历,就可以得到答案。

源代码:

C++:

class Solution {
public:
    string decodeMessage(string key, string message) {
        char cur = 'a';
        unordered_map<char, char> rules;

        for (char c: key) {
            if (c != ' ' && !rules.count(c)) {
                rules[c] = cur;
                ++cur;
            }
        }

        for (char& c: message) {
            if (c != ' ') {
                c = rules[c];
            }
        }

        return message;
    }
};

JAVA:

class Solution {
    public String decodeMessage(String key, String message) {
        char cur = 'a';
        Map<Character, Character> rules = new HashMap<Character, Character>();

        for (int i = 0; i < key.length(); ++i) {
            char c = key.charAt(i);
            if (c != ' ' && !rules.containsKey(c)) {
                rules.put(c, cur);
                ++cur;
            }
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < message.length(); ++i) {
            char c = message.charAt(i);
            if (c != ' ') {
                c = rules.get(c);
            }
            sb.append(c);
        }

        return sb.toString();
    }
}

总结:

一、学到了什么

这道题目提供了一次优秀的学习机会,涉及到几个关键的编程和计算机科学概念:

1. 哈希表/数组映射

哈希表是一种数据结构,用于存储键值对。在这个解决方案中,使用了一个字符数组作为一个简单的哈希表,其中索引是字符的ASCII值,而值是该字符的映射。学会如何使用哈希表是解决类似问题的关键。

2. 字符串操作

解决这个问题需要对字符串进行遍历和修改。需要理解如何使用字符串和数组,如何遍历它们,如何修改字符串中的单个字符,这是编程中常见的需求。

3. 条件判断

解决方案中使用了条件判断来检查字符是否为空格,以及字符是否已经被映射。掌握条件判断语句对于实现更复杂的逻辑至关重要。

4. 循环

通过循环来遍历字符串是一个基本的编程概念。在这个问题中,我们看到了如何使用for循环来遍历字符串,并基于某个条件来修改它。

5. 内存管理

通过memset函数初始化数组,是C语言中内存管理的一个例子。理解如何初始化和管理内存是使用低级语言(如C)编程的一个重要方面。

6. 解密和编码

这个问题还涉及到简单的解密概念。虽然这个问题的加密/解密过程比现实中的要简单得多,但它提供了对于如何使用映射和转换来解密文本的基础理解。

7. 优化

这个方案中直接修改了输入的消息字符串,避免了使用额外空间。这是一个优化,它减少了程序的空间复杂度。在实际问题中,考虑如何优化代码以减少空间和时间复杂度是非常重要的。

8. 测试与调试

给定代码中的小错误(循环中的逗号),表明测试和调试是编写代码的重要部分。即使代码看起来很简单,也可能包含错误或bug,因此测试代码以确保其正确性是必要的。

总结

这道题目是学习和练习基础编程概念、数据结构操作、内存管理和简单加密解密概念的好机会。同时,它也强调了测试、调试和优化的重要性。

二、思想思维和方法:

通过这道题目和题解,我们可以学到以下几点思想、方法和思维:

### 1. **问题分解**
学习将问题分解成较小、更易管理的部分。例如,此问题可以分解为两个主要部分:构建映射表和解密消息。将问题分解成小部分有助于更清晰地理解问题并逐步解决它。

### 2. **简化问题**
在构建解决方案时,寻找将问题简化的方法。在这个例子中,使用字符数组作为简单的哈希表可以简化解决方案,而不需要复杂的数据结构。

### 3. **避免不必要的复杂性**
优先寻求简单、直接的解决方案。在这个例子中,我们看到如何通过直接修改输入字符串来避免额外的空间复杂性。

### 4. **自顶向下的思维**
首先构思整体解决方案的框架,然后详细处理实现的具体细节。例如,先确定需要构建映射表,然后实现具体的映射表构建过程。

### 5. **迭代改进**
开始于基础解决方案,并逐步优化。例如,首先实现一个基本的解密过程,然后考虑如何减少空间和时间复杂性。

### 6. **注意边界条件**
仔细考虑并处理输入的各种可能性,包括空字符串、特殊字符等。这有助于编写健壮的代码,能够处理各种情况。

### 7. **测试驱动的开发**
从题解中的错误,我们可以学到编写测试用例并进行严格测试的重要性。这有助于早期发现和修复错误,确保代码的正确性。

### 8. **数据结构和算法知识**
了解并熟悉各种数据结构和算法是解决此类问题的基础。例如,了解哈希表、字符串操作和基础算法有助于更有效地解决问题。

### 9. **逐步推导**
对于映射表的构建和解密过程,应该进行逐步推导,一步一步验证每个过程的正确性,这是一种解决问题和调试的有效方法。

### 10. **细心与耐心**
编程时要保持细心,对待每一处细节都不能马虎,同时在调试过程中要保持耐心,逐一排查可能的错误来源。

总之,这道题目提供了对基本编程原则、问题解决方法和计算机科学基础知识的实践机会,通过实际操作,可以加深对这些概念和技巧的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

golang学习笔记(一):基础入门

基础入门 菜鸟教程Go语言环境安装 GoLand开发工具下载 Gin web开发框架 Go 语言流行 ORM 框架 GORM 使用介绍 如何使用Go语言连接分布式MySQL数据库 Go语言依赖搜索网站&#xff0c;类似Maven 添加依赖&#xff1a; 基础知识 1.关键字 go 开启协程执行调用语句/方法。 def…

Spring面试题9:Spring的BeanFactory和FactoryBean的区别和联系

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一说Spring的BeanFactory和FactoryBean的区别和联系 区别:BeanFactory是一个工厂接口,主要负责管理和创建Bean实例。它是Spring提供的最底层的…

Centos7虚拟机硬盘扩容 + 修改Docker默认存储位置

文章目录 前言一、Centos7虚拟机硬盘扩容1.1 具体步骤二、修改Docker默认存储位置2.1 挂载目录2.2 修改路径 前言 Vmware 虚拟机开机时提示&#xff1a;虚拟机根目录系统内存不足&#xff0c;可能原因是 /var/lib/docker 目录占用的磁盘空间不够了。本文记录虚拟机磁盘扩容过程…

Doris数据库FE——SQL handleQuery

SQL解析在下文中指的是将一条sql语句经过一系列的解析最后生成一个完整的物理执行计划的过程。这个过程包括以下四个步骤&#xff1a;词法分析、语法分析、生成逻辑计划、生成物理计划。Doris SQL解析具体包括了六个步骤&#xff1a;词法分析&#xff0c;语法分析、语义分析&am…

文件系统详解

目录 文件系统&#xff08;1&#xff09; 第一节文件系统的基本概念 一、文件系统的任务 二、文件的存储介质及存储方式 三、文件的分类 第二节 文件的逻辑结构和物理结构 一、文件的逻辑结构 二、文件的物理结构 文件系统&#xff08;2&#xff09; 第三节 文件目…

Oracle for Windows安装和配置——Oracle for Windows数据库创建及测试

2.2. Oracle for Windows数据库创建及测试 2.2.1. 创建数据库 1&#xff09;启动数据库创建助手&#xff08;DBCA&#xff09; 进入%ORACLE_HOME%\bin\目录并找到“dbca”批处理程序&#xff0c;双击该程序。具体如图2.1.3-1所示。 图2.1.3-1 双击“%ORACLE_HOME%\bin\dbca”…

mac 配置 httpd nginx php-fpm 详细记录 已解决

在日常mac电脑 开发php项目一直是 httpd 方式 运行&#xff0c;由于有 多版本 运行的需求&#xff0c;docker不想用&#xff0c;索性用 php-fpm进行 功能处理。上次配置 是好的&#xff0c;但是感觉马马虎虎&#xff0c;这次 配置底朝天。因为配置服务器&#xff0c;几乎也都是…

9. Java字符串支持正则表达式的方法

Java —— String字符串 1. 正则表达式2. String正则API3. Object类和toString方法4. equals方法5. 包装类及Number 1. 正则表达式 正则表达式&#xff08;Regular Expression&#xff09;&#xff1a;简称为Regex或RegExp&#xff0c;是一种用于描述字符串模式的工具 作用&…

Jmeter性能测试步骤

Jmeter多用户并发测试 第1步&#xff0c; 在安装目录下的bin文件夹下打开Jmeter&#xff0c;如下图 第2步&#xff0c;新建一个线程组 第3步&#xff0c;新建一个HTTP请求&#xff0c;这里给这个请求重命名为getMsgSum 设置HTTP请求的IP端口&#xff0c;以及路径等 第4步&#…

SoftwareTest3 - 要了人命的Bug

软件测试基础篇 一 . 如何合理的创建一个 Bug二 . Bug 等级2.1 崩溃2.2 严重2.3 一般2.4 次要 三 . Bug 的生命周期四 . 跟开发产生争执应该怎么解决 Hello , 大家好 , 又给大家带来新的专栏喽 ~ 这个专栏是专门为零基础小白从 0 到 1 了解软件测试基础理论设计的 , 虽然还不足…

C++ -- 特殊类设计

目录 设计一个类&#xff0c;不能被拷贝 C98的做法 C11的做法 设计一个类&#xff0c;只能在堆上创建对象 实现方式1 实现方式2 设计一个类&#xff0c;只能在栈上创建对象 实现方式1 方式1的优化 实现方式2 设计一个类&#xff0c;不能被继承 设计模式 什么是设计…

怒刷LeetCode的第9天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;双指针 方法二&#xff1a;递归 方式三&#xff1a;迭代 方法四&#xff1a;优先队列 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;贪心算法 方法二&#xff1a;数学方法 方法三&#xff1…

iOS应用中的内存泄漏问题解决

解决iOS应用中的内存泄漏问题是非常重要的&#xff0c;因为内存泄漏可能导致应用变得缓慢或不稳定。以下是一些解决iOS内存泄漏问题的工具和方法&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 工具&…

HTML5day02综合案例2

案例展示 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>注册信息</title> </head> &l…

MC互联网联机frp实现

我使用的是java版本的MC&#xff0c;联机方式如下。只是一个简单的笔记&#xff0c;所以只说重点。 主机开启局域网 整合包中自带的局域网联网插件&#xff1a;Lan Server Properties 1.10.1 在线模式选择关闭&#xff0c;不然非正版用户无法连接。 frp 具体不说了&#x…

基于MUSIC算法的二维超声波成像matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、基本原理 4.2、数学公式 4.3、实现过程 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ..........................................…

前端react 18.2整合ckeditor富文本编辑器——配置插件、自定义toolbar工具栏

文章目录 ⭐前言⭐引入ckeditor⭐npm 包引入⭐cdn资源引入 ⭐自定义插件&#x1f496; 自定义yma16配置插件 ⭐总结⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于前端react整合ckeditor——配置插件、自定义toolbar工具栏。 react系列往期文章&#xff…

sylixos 5.0.4 ecs

sylixos 5.0.4 ecs 2692407267qq.com&#xff0c;更多内容请见http://user.qzone.qq.com/2692407267/

人类的态势感知可分为先验、似然、后验的三部分

人类的态势感知可以分为先验、似然和后验三个部分。这些部分描述了在不同阶段对环境的感知和理解。先验感知&#xff1a;先验感知基于先前的知识、经验和先验信息来理解环境。它利用已有的知识和模型&#xff0c;进行预测和推断&#xff0c;从而形成对当前环境的初步认知。先验…

Go 并发可视化解释 - sync.Mute

在学习 Go 编程语言时&#xff0c;您可能会遇到这句著名的格言&#xff1a;“不要通过共享内存来进行通信&#xff1b;相反&#xff0c;通过通信来共享内存。” 这句话构成了 Go 强大并发模型的基础&#xff0c;其中通道&#xff08;channels&#xff09;作为协程之间的主要通信…