代码随想录算法训练营day8 | 344.反转字符串、541.反转字符串 II、卡码网:54.替换数字

文章目录

    • 344.反转字符串
      • 思路
    • 541.反转字符串 II
      • 思路
    • 卡码网:54.替换数字
      • 思路
      • 复习:字符串 vs 数组
    • 总结

今天是字符串专题的第一天,主要是一些基础的题目

344.反转字符串

建议: 本题是字符串基础题目,就是考察 reverse 函数的实现,同时也明确一下 平时刷题什么时候用 库函数,什么时候 不用库函数

题目链接:344. 反转字符串 - 力扣(LeetCode)

本题要编写一个函数,将输入的字符串反转过来。这道题考察的是reverse函数的实现原理,就不要直接使用reverse函数解题了。如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数如果库函数仅仅是 解题过程中的一小部分,并且你已经很清楚这个库函数的内部实现原理的话,可以考虑使用库函数

思路

这道题的思想与 206.反转链表 相同,都是使用双指针法字符串也是一种数组,所以元素再内存中是连续存储的,因此 反转字符串 比 反转链表要容易

整体思路

定义left、right指针(就是索引下标)

  • 初始:left指向字符串的第一个元素,right指向字符串的最后一个元素
  • 反转:交换s[left]s[right],然后收缩这两个指针:left++right--
  • 循环条件:当right > left时,就继续执行while循环,交换s[left]s[right]

反转过程 如图:

344.反转字符串

可以使用库函数swap执行交换操作,swap函数可以有两种实现

  • 一种是常见的交换数值:

    char tmp = s[left];
    s[left] = s[right];
    s[right] = tmp;
  • 另一种是通过位运算,CSAPP第二章的位运算部分讲过这个:

    s[left] ^= s[right];
    s[right] ^= s[left];
    s[left] ^= s[right]

代码实现

class Solution {
public:void reverseString(vector<char>& s) {int left = 0, right = s.size()-1;while(right > left){swap(s[left], s[right]);right--;left++;}}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

541.反转字符串 II

建议:本题又进阶了,自己先去独立做一做,然后在看题解,对代码技巧会有很深的体会。

题目链接:541. 反转字符串 II - 力扣(LeetCode)

思路

这道题就是设置了一点障碍,思想与前一道题是相同的,只是反转并不是一次完成的,每2k个字符才能反转一次。一种思路是将字符串“分割”:遍历字符串,设置count计数,每计数2k个字符,就进入反转程序,对这2k个字符的区间进行反转。另一种思路是在遍历的同时直接进行反转,对于一些特殊的情况我们单独判断。第二种思路更自然,我们选择第二种思路解题

整体思路

如果使用 344.反转字符串 中的双指针法反转字符串,我们使用current表示当前遍历到的位置:

初始:current指向字符串的第一个元素

反转:每次while循环开始,left赋为current,是反转区间的第一个元素。关键是确定right,剩余字符串的长度length 与 k 的大小比较决定了right的位置

  • 若length < k,题目要求将剩余字符全部反转。此时right = s.size()-1,指向这个字符串的末尾
  • 若k <= length < 2k,题目要求反转前 k 个字符,其余字符保持原样,那么right = current + k -1,指向反转区间的第k个字符
  • 若k >= 2k,题目要求反转这 2k 个字符中的前 k 个字符,则right = current + k - 1,指向反转区间的第k个字符

当完成一个区间的反转后,current向后移动2k个元素,即current += 2*k。如果 leng < k 或 k <= length < 2k,则移动2k个元素后current > s.size(),因此不会进入下一次循环。这保证了k <= length < 2k时,只反转前k个字符,而其他字符保持原样

代码实现

class Solution {
public:string reverseStr(string s, int k) {// 使用current指针指向当前遍历到的位置int current = 0;int left, right;while(current < s.size()){left = current;int length = s.size() - current;if(length <  k){right = s.size()-1;}else   // 剩下的两种情况都是反转前k个字符{right = current + k - 1;}while(right > left){swap(s[left], s[right]);right--;left++;}current += 2*k; // 如果剩余字符小于k个 或 小于 2k 但大于或等于 k 个,则下次循环直接退出了}return s;}
};

本题的关键在于对反转区间的划分,而不在于如何反转,因此可以直接使用reverse函数

代码实现

class Solution {
public:string reverseStr(string s, int k) {for (int i = 0; i < s.size(); i += (2 * k)) {// 1. 每隔 2k 个字符的前 k 个字符进行反转// 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符if (i + k <= s.size()) {reverse(s.begin() + i, s.begin() + i + k );} else {// 3. 剩余字符少于 k 个,则将剩余字符全部反转。reverse(s.begin() + i, s.end());}}return s;}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

卡码网:54.替换数字

建议:对于线性数据结构,填充或者删除,后序处理会高效的多。好好体会一下

这道题是Carl刷题网站上的,题目链接:54. 替换数字(第八期模拟笔试) (kamacoder.com)

这个题目需要自己补全所有代码,包括导入库,实现main函数

思路

本题使用双指针法。我们首先遍历一遍字符串,统计所有出现过的数字字符数量,然后对这个字符串做扩展,预留需要填充的大小,如图所示:

img

然后我们利用双指针法在这个字符串上进行数字的替换,我们需要明确这两个指针的含义:

  • 第一个指针指向旧字符串的末尾(未扩展前的字符串),用来判断旧字符串中的字符是否为数字

  • 第二个指针指向新字符串的末尾,根据第一个指针指向的字符,填充相应的元素:

    • 如果第一个指针指向的是字母字符,则第二个指针填充这个字母字符,这两个指针同时向前移动
    • 如果第一个指针指向的是数字字符,则第二个指针填充“number”,然后两个指针同时向前移动

    其实整个过程可以看成是一个映射:

    • 第一个指针指向字母字符 -> 第二个指针填充字母字符
    • 第一个指针指向数字字符 -> 第二个指针填充"number"

思路如图:

img

你会发现我们是从后向前填充这个数组的,从前向后填充不行吗?

从前向后填充就是O(n2)的算法了,因为每次添加元素都要将添加元素之后的所有元素整体向后移动,注意:我们的旧字符串 在 新字符串的前面部分,因此从头填充是需要将旧字符串依次后移,否则会丢失旧字符串中的字符

其实很多数组填充类的问题,其做法都是先预先给数组扩容带填充后的大小,然后在从后向前进行操作

这么做有两个好处:

  1. 不用申请新数组
  2. 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题

代码实现

#include<iostream>
using namespace std;int main()
{// 由于题目可能逐个输入字符串,所以我们使用while循环读取输入的字符string s;while(cin >> s){int sOldIndex = s.size()-1; // 第一个指针,初始指向旧字符串的末尾元素int count = 0;  // 统计数字字符的个数for(int i=0; i<s.size(); ++i){if(s[i] >= '0' && s[i] <= '9'){count++;}}// 扩充字符串s的大小,也就是将每个数字替换为"number"之后的字符串大小s.resize(s.size() + count*5);int sNewIndex = s.size() - 1;// 从后向前将数字替换为"number",字母直接映射过来while(sOldIndex >= 0){if(s[sOldIndex] >= '0' && s[sOldIndex] <= '9'){s[sNewIndex--] = 'r';s[sNewIndex--] = 'e';s[sNewIndex--] = 'b';s[sNewIndex--] = 'm';s[sNewIndex--] = 'u';s[sNewIndex--] = 'n';}else{s[sNewIndex--] = s[sOldIndex];}// sOldIndex向前移动一位sOldIndex--;}cout << s << endl;}
}

复习:字符串 vs 数组

复习一下字符串和数组有什么差别:

字符串是若干字符组成的有限序列,也可以理解是一个字符数组,但是很多语言对字符串做了特殊的规定,接下来说一说C/C++中的字符串

在C语言中,把一个字符串存入一个数组时,也把结束符 '\0’存入数组,并以此作为该字符串是否结束的标志。

例如这段代码:

char a[5] = "asd";
for (int i = 0; a[i] != '\0'; i++) {
}

在C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用’\0’来判断是否结束。

例如这段代码:

string a = "asd";
for (int i = 0; i < a.size(); i++) {
}

那么vector< char > 和 string 又有什么区别呢?

其实在基本操作上没有区别,但是 string提供更多的字符串处理的相关接口,例如string 重载了+,而vector却没有。

所以想处理字符串,我们还是会定义一个string类型

总结

今天的题目比较简单,关键是双指针法字符串的操作,最后一道题是一道经典的数组扩充题目,需要好好体会

字符串的一些常用操作如下:

#include <iostream>
#include <string>int main() {// 1. 构造字符串std::string s1; // 默认构造函数std::string s2("Hello, World!"); // 从C风格字符串构造std::string s3(s2); // 复制构造函数std::string s4(s2, 7, 5); // 从 s2 的第 7 个字符开始取 5 个字符// 2. 赋值操作s1 = s2; // 赋值运算符s3.assign("Hi there!"); // assign 赋值// 3. 访问元素char c1 = s2[1]; // 使用 operator[] 访问元素,结果为 'e'char c2 = s2.at(1); // 使用 at 访问元素,结果为 'e'char c3 = s2.front(); // 访问第一个字符,结果为 'H'char c4 = s2.back(); // 访问最后一个字符,结果为 '!'// 4. 迭代器std::cout << "Using iterator: ";for (auto it = s2.begin(); it != s2.end(); ++it) {std::cout << *it; // 使用迭代器输出字符串}std::cout << std::endl;std::cout << "Using reverse iterator: ";for (auto rit = s2.rbegin(); rit != s2.rend(); ++rit) {std::cout << *rit; // 使用反向迭代器输出字符串}std::cout << std::endl;// 5. 容量相关bool isEmpty = s2.empty(); // 检查字符串是否为空,结果为 falsestd::size_t size = s2.size(); // 获取字符串长度,结果为 13std::size_t length = s2.length(); // 获取字符串长度,结果为 13std::size_t capacity = s2.capacity(); // 获取容量s2.reserve(50); // 预留至少 50 个字符的存储空间s2.resize(10); // 将字符串大小调整为 10// 6. 修改操作s2.clear(); // 清空字符串s2 = "Hello, World!"; // 重新赋值s2.push_back('!'); // 在末尾添加字符s2.pop_back(); // 移除末尾的字符s2.insert(5, " World"); // 在位置 5 插入字符串s2.erase(5, 6); // 从位置 5 开始删除 6 个字符s2.replace(7, 5, "C++"); // 从位置 7 开始替换 5 个字符为 "C++"// 7. 查找操作std::size_t pos = s2.find("C++"); // 查找子字符串 "C++" 的第一次出现位置std::size_t rpos = s2.rfind("l"); // 从后向前查找字符 'l' 的位置std::size_t fpos = s2.find_first_of("aeiou"); // 查找第一个元音字母的位置std::size_t lpos = s2.find_last_of("aeiou"); // 查找最后一个元音字母的位置std::size_t fnpos = s2.find_first_not_of("Hello"); // 查找第一个不在 "Hello" 中的字符位置std::size_t lnpos = s2.find_last_not_of("Hello"); // 查找最后一个不在 "Hello" 中的字符位置// 8. 子字符串std::string sub = s2.substr(7, 5); // 从位置 7 开始获取长度为 5 的子字符串// 9. 比较操作int result = s2.compare(s3); // 比较 s2 和 s3 的内容// 输出结果std::cout << "s2: " << s2 << std::endl;std::cout << "pos: " << pos << std::endl;std::cout << "rpos: " << rpos << std::endl;std::cout << "fpos: " << fpos << std::endl;std::cout << "lpos: " << lpos << std::endl;std::cout << "fnpos: " << fnpos << std::endl;std::cout << "lnpos: " << lnpos << std::endl;std::cout << "sub: " << sub << std::endl;std::cout << "compare result: " << result << std::endl;return 0;
}

感谢chatgpt😸

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

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

相关文章

docker挂载部署reids6.2.1

1.拉取镜像 docker pull redis:6.2.12.创建挂在目录&#xff08;根据自己要求修改具体目录&#xff09; mkdir -p /home/admin/redis/{data,conf}3.在/home/admin/redis/conf目录下创建redis.conf文件 cd /home/admin/redis/conf touch redis.conf4.复制下面文本到redis.conf…

浅析JWT原理及牛客出现过的相关面试题

原文链接&#xff1a;https://kixuan.github.io/posts/f568/ 对jwt总是一知半解&#xff0c;而且项目打算写个关于JWT登录的点&#xff0c;所以总结关于JWT的知识及网上面试考察过的点 参考资料&#xff1a; Cookie、Session、Token、JWT_通俗地讲就是验证当前用户的身份,证明-…

不断把别人“装”进我们的灵魂口袋

嘿&#xff0c;朋友们&#xff01;今天我们来聊聊一种超酷的能量——本色示人。这不是什么秘密武器&#xff0c;但它比任何超能力都来得实在。 第一部分&#xff1a;本色示人&#xff0c;能量界的“超级赛亚人” 1.1 坦诚的超能力 想象一下&#xff0c;如果你的内心强大到可以…

Window部署Ollama+Qwen2.0+Open-WebUI

文章目录 Windows下安装Docker安装Docker检查是否安装成功, 出现版本即为安装成功安装Ollama启动 Ollama 并拉取模型(选做) 修改默认地址和端口(选做) Ollama 进行跨域配置安装open-webui Windows下安装Docker 准备条件 开启Hyper-V&#xff0c;在“启用或关闭Windows功能”里…

C语言 #指针数组 #数组指针 #数组参数、指针参数

文章目录 前言 一、指针数组 1、概念&#xff1a; 2、指针数组有什么用呢&#xff1f; 二、数组指针 1、数组指针的定义 2、数组名与 &数组名 的区别 3、数组指针如何初始化&#xff1f; 4、数组指针的用法 三、根据代码区分 指针数组 和 数组指针 四、数组参数、指针参数 …

Shell编程之正则表达式与文本三剑客

目录 一、正则表达式 1.引言--什么是正则表达式 1.1正则表达式的功能 2.基础正则表达式&#xff08;BRE&#xff09; 2.1特殊字符 2.2定位符 2.3非打印字符 3.扩展正则表达式(ERE) 4.元字符操作的案列 二、命令小工具 1.cut&#xff1a;列截取工具 2.sort排序 …

【Android】使用ViewPager2与TabLayout实现顶部导航栏+页面切换

【Android】使用ViewPager2与TabLayout实现顶部导航栏&#xff0b;页面切换 TabLayout与ViewPager2概述 TabLayout TabLayout 是 Android 支持库中的一个组件&#xff0c;它是 Design 支持库的一部分。TabLayout 提供了一个水平的标签页界面&#xff0c;允许用户在不同的视图…

CogVideo 实测,智谱「清影」AI视频生成,全民免费,连 API 都开放了!

不得不说&#xff0c;AI 视频生成界最近非常火热~ 前有快手「可灵」开放内测&#xff0c;一下子带火了老照片修复&#xff0c;全网刷屏&#xff1a; 怕是你还没拿到内测资格&#xff0c;被称为 “国货之光” 的「可灵」就结束了免费无限量模式。每天只有66点的免费额度&#x…

鸿蒙(API 12 Beta2版)【创建NDK工程】

创建NDK工程 下面通过DevEco Studio的NDK工程模板&#xff0c;来演示如何创建一个NDK工程。 说明 不同DevEco Studio版本的向导界面、模板默认参数等会有所不同&#xff0c;请根据实际工程需要&#xff0c;创建工程或修改工程参数。 通过如下两种方式&#xff0c;打开工程创…

2024-07-27 Unity Excel —— 使用 EPPlus 插件读取 Excel 文件

文章目录 1 前言2 项目地址3 使用方法3.1 写入 Excel3.2 读取 Excel3.3 读写 csv 文件 4 ExcelSheet 代码 1 前言 ​ 前几日&#xff0c;一直被如何在 Unity 中读取 Excel 的问题给困扰&#xff0c;网上搜索相关教程相对古老&#xff08;4、5 年以前了&#xff09;。之前想用 …

【openavis】明厨亮灶算法仓

明厨亮灶算法仓主要用于学校食堂&#xff0c;餐厅等饮食卫生安全监管场景&#xff0c;目前包含的算法如下&#xff1a; 算法类型 算法卡片 明厨亮灶算法仓 老鼠检测 垃圾桶未盖 厨师服检测 厨师帽检测 口罩检测 手套检测 动火离人 1. 算法规格介绍&#xff1a; 算…

基于STM32瑞士军刀--【FreeRTOS开发】学习笔记(一)|| RISC / 底层代码执行步骤 / 汇编指令

本篇文章基于韦东山老师讲课笔记和自己理解编写。 RISC ARM芯片属于精简指令集计算机(RISC&#xff1a;Reduced Instruction Set Computing)&#xff0c;它所用的指令比较简单&#xff0c;有如下特点&#xff1a; ① 对内存只有读、写指令 ② 对于数据的运算是在CPU内部实现 …

你在找提升效率的解决方案还是追求效果的解决方案

企业在寻求“解决方案”时&#xff0c;最好先想清楚&#xff0c;你是想提升某项工作的效率&#xff0c;还是要改善某项工作的效果&#xff1f; 提升效率的解决方案主要是为了在保证质量的前提下提升某项确定工作的完成速度。以政务解决方案为例&#xff1a;当任何人都能通过移…

HAL STM32 SPI/ABZ/PWM方式读取MT6816磁编码器数据

HAL STM32 SPI/ABZ/PWM方式读取MT6816磁编码器数据 &#x1f4da;MT6816相关资料&#xff08;来自商家的相关资料&#xff09;&#xff1a; 资料&#xff1a;https://pan.baidu.com/s/1CAbdLBRi2dmL4D7cFve1XA?pwd8888 提取码&#xff1a;8888&#x1f4cd;驱动代码编写&…

某量JS逆向

https://chat.sensetime.com/wb/chat 目录 一、发起请求 二、观察发现只有入参 __data__ 进行了加密&#xff0c;返回是明文 三、 观察JS调用栈 四、从JS中搜索 __data__ 五、使用XHR对Ajax请求进行断点 六、再次发起请求就会断点拦住请求 七、对XHR入口分析 八、逐个…

【C++】选择结构- 嵌套if语句

嵌套if语句的语法格式&#xff1a; if(条件1) { if(条件1满足后判断是否满足此条件) {条件2满足后执行的操作} else {条件2不满足执行的操作} } 下面是一个实例 #include<iostream> using namespace std;int main4() {/*提示用户输入一个高考分数&#xff0c;根据分…

花几千上万学习Java,真没必要!(二十九)

1、基本数据类型包装类&#xff1a; 测试代码1&#xff1a; package apitest.com; //使用Integer类的不同方法处理整数。 //将字符串转换为整数&#xff08;parseInt&#xff09;和Integer对象&#xff08;valueOf&#xff09;&#xff0c; //将整数转换回字符串&#xff08;…

【计算机网络】DNS命令练习与抓包分析实验

一&#xff1a;实验目的 1&#xff1a;掌握DNS缓存的清除方法&#xff0c;了解DNS缓存的作用和影响。 2&#xff1a;熟悉nslookup和dig等DNS查询工具的使用&#xff0c;理解DNS查询的基本原理和过程。 3&#xff1a;通过抓包和分析&#xff0c;深入了解DNS查询和响应消息的格…

html+css 实现悬浮按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

Python+Flask+MySQL+日线指数与情感指数预测的股票信息查询系统【附源码,运行简单】

PythonFlaskMySQL日线指数与情感指数预测的股票信息查询系统【附源码&#xff0c;运行简单】 总览 1、《股票信息查询系统》1.1 方案设计说明书设计目标工具列表 2、详细设计2.1 登录2.2 程序主页面2.3 个人中心界面2.4 基金详情界面2.5 其他功能贴图 3、下载 总览 自己做的项…