当前位置: 首页 > news >正文

字符串相关OJ题解析(图文并茂+过程演示)


个人主页

🎬 个人主页:Vect个人主页

🎬 GitHub:Vect的代码仓库

🔥 个人专栏: 《数据结构与算法》《C++学习之旅》《计算机基础》

⛺️Per aspera ad astra.


文章目录

  • 1. 把字符串转成整数(atoi)
  • 2. 字符串相加
  • 3. 字符串相乘
  • 4. 字符串中的第一个唯一字符
    • 思路一
    • 思路二
  • 5. 验证回文串

1. 把字符串转成整数(atoi)

把字符串转成整数(atoi)
根据题目要求,把字符串转成32位有符号整数,需要考虑下列情况:
1. 首部空格: 直接删除
2. 符号位: 三种情况, + - “无符号”, 可以创建一个sign变量保存符号位,返回前判断一下正负就好
3. 非数字字符: 遇到第一个非数字字符就停止
4. 数字字符:

  • 字符转数字:这个数字的ascii - 0对应的ASCII
  • 数字拼接:转成十进制数,假设当前位字符是ccc,当前位数字是xxx,相加结果是resresres,那么就有如下公式:res=10×res+xres = 10 \times res + xres=10×res+x

因为我们是从左到右按十进制把数字一位一位“接在末尾地构造整数。十进制是以 10 为底的位权制:每往左一位,权值就×10。因此当读到下一位数字x(0~9)时,现有结果 res 里所有位都要整体左移一位(位权×10),再把新的一位x加到个位上

如图所示:
在这里插入图片描述

  1. 越界检查:[−231,231−1][-2^{31}, 2^{31} - 1 ][231,2311],即是[-2,147,483,648,2,147,483,647]
    我们在每轮数字拼接前都要判断resresres在拼接后是否超过2,147,483,647,若超过边界就加上符号位直接返回即可
    这里讨论一下越界情况:
    • res×10>2,147,483,647res \times 10 > 2,147,483,647res×10>2,147,483,647,拼接的时候越界
    • res=2,147,483,640,res+x>2,147,483,647res = 2,147,483,640 ,res + x > 2,147,483,647res=2,147,483,640res+x>2,147,483,647,这种情况x=8或者x=9x=8 或者 x=9x=8或者x=9
      解释一下:

resresres每次都要×10\times 10×10,要确保res×10res\times 10res×10之后不能越界
res=214748364res = 214748364res=214748364时,res×10=214748640res \times 10=214748640res×10=214748640,没有越界
现在要加上数字位x,也要保证不能越界,所以x<=7x<=7x<=7


总结一下思路:

  • 遇到空格删除
  • 遇到符号保留
  • 遇到数字进行拼接,并检查边界
  • 遇到第一个非数字字符,break

代码实现:

class Solution {
public:int myAtoi(string str) {int length = str.size();     // 原字符串长度int idx = 0;                 // 当前扫描位置int res = 0;                 // 逐位累积出来的“绝对值部分”(不带符号)int sign = 1;                // 记录符号,默认正int border = INT_MAX / 10;   // 溢出预判用的安全边界(214748364):// 1. 处理空格和符号// 跳过前导空格;这里仅忽略 ' ',不处理 \t \n 等while (idx < length && str[idx] == ' ') ++idx;// 如果全是空格,直接返回 0if (idx == length) return 0;// 读取一个可选符号位if (str[idx] == '+' || str[idx] == '-') {sign = (str[idx] == '-') ? -1 : 1; // 记录符号++idx;                             // 移过符号}// 2. 拼接数字// 从 idx 开始,连续读取数字字符;遇到第一个非数字就停止for(int j = idx; j < length; j++){// 非数字字符:结束数字段(只读第一段连续数字)if(str[j] < '0' || str[j] > '9') break;// 溢出预判// res 代表当前已累积的正数部分,准备并入下一位 str[j]// 两种会超上界(INT_MAX=2147483647)的情况:// 1) res > 214748364(即 border),res*10 一定超// 2) res == 214748364 且下一位 > 7(INT_MAX % 10),res*10 + x 会超if(res > border || (res == border && str[j] > '7'))// 一旦会超:根据 sign 返回相应端点(正:INT_MAX;负:INT_MIN)return sign == 1 ? INT_MAX : INT_MIN;// 安全:把当前位并入(十进制每进一位×10,再加本位数字)res = 10 * res + (str[j] - '0');}// 乘上符号返回结果return sign * res;}
};

2. 字符串相加

字符串相加
思路:用一个新的数组存放相加后的结果,从后往前遍历两个字符串,将检索到的数字字符分别转换成int类型后,再相加,此时会出现两种情况:
1. 进位,保留个位数字,存到数组里,下一位多加1
2. 不进位,直接将结果存到数组
加完之后要判断最高位是否还要进位
最后逆转数组即可
在这里插入图片描述
代码实现:

class Solution {
public:// 思路:模拟竖式加法。从两个字符串末尾(最低位)开始逐位相加//       用up记录进位,把每一轮的个位先 push 到结果字符串尾部//       循环结束后若仍有进位再补 '1',最后整体 reverse 得到高位在前的正确结果string addStrings(std::string num1, std::string num2) {string numArr;       // 结果暂存区:按低位→高位的顺序依次push字符 最终再反转int up = 0;               // 进位 // i/j 当前处理的位 从最右端开始int i = num1.size() - 1;int j = num2.size() - 1;// 任一字符串还有未处理的位就继续// 不用拆两段剩余位循环while (i >= 0 || j >= 0) {// 取 num1 当前位数字 a 若越界则取 0int a = (i >= 0) ? (num1[i] - '0') : 0;// 取 num2 当前位数字 b 若越界则取 0int b = (j >= 0) ? (num2[j] - '0') : 0;// 本轮求和int ones = a + b + up;// 更新本轮进位:十进制进位等于对 10 取整除(0 或 1)up = ones / 10;// 本位应当写入的“个位数”是对 10 取模的结果ones %= 10;// 把本位结果转成字符追加到 numArr 尾部numArr.push_back('0' + ones);--i;--j;}// 所有位处理完毕后,如果还有进位(如 999 + 1),需要再补一个最高位 '1'if (up) {numArr.push_back('1');}// 当前 numArr 为逆序,反转成高位在前reverse(numArr.begin(), numArr.end());return numArr;}
};

3. 字符串相乘

字符串相乘
思路和字符串相加类似:

  1. 开一个ansArr数组存储相乘的结果
    数组开多大? num1mmm大小,num2nnn大小
    m+nm+nm+n(最大) 最小呢?m+n−1m+n-1m+n1
    如图所示:
    在这里插入图片描述
  2. 按照竖式乘法的习惯,inum1的索引,jnum2的索引,先固定j,从后往前遍历num1[i]num2[j]分贝相乘,相乘结果有两种:
  • 不用进位,结果存在ansArr[i+j+1]
  • 需要进位,十位结果存在ansArr[i+j]
  1. 不断循环,直到两个数组都遍历完成
  2. 判断是否有前导0,将数字转为字符串输出
    具体过程如图所示:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
本质就是做了三次加法:738+6150+49200738+6150+49200738+6150+49200

代码实现:

class Solution {
public:string multiply(string num1, string num2) {if(num1 == "0" || num2 == "0") return "0"; // 0*任何数都为0int sizeNum1 = num1.size();int sizeNum2 = num2.size();// 结果最多为 m+n 位,因此开一个 m+n 的整型桶数组存每一位(高位在前,低位在后)vector<int> ansArr(sizeNum1 + sizeNum2, 0); // 存相乘结果的数组int res = 0; // 暂存“当前位置已有值 + 本次乘积”的和(便于取个位与进位)// i/j 当前处理的位置,从最右端(最低位)开始向左int i = sizeNum1 - 1, j = sizeNum2 - 1;// 双层循环:num1 的每一位 × num2 的每一位for(; i >= 0; i--){int x = num1[i] - '0';                   for(j = sizeNum2 - 1; j >= 0; j--){      // 每次外层循环都把 j 复位到末尾int y = num2[j] - '0';              /* 对齐思路:x 位于 i,下标从 0 开始的话,x*y 的结果落在 ansArr 的*  “个位”位置 p2 = i + j + 1,“进位”位置 p1 = i + j*  ansArr[p2] 可能之前已累加过其他对齐到 p2 的乘积,所以要先加上*/res = ansArr[i + j + 1] + x * y;// 把“和”的个位写回 p2(当前位置),把“和”的十位以上加到 p1(前一位,作为进位累加)ansArr[i + j + 1] = res % 10; // 处理相乘的“个位”ansArr[i + j] += res / 10;    // 进位累加到前一位}}// 去掉可能的前导 0(最高位为 0 时跳过它)int tmp = ansArr[0] == 0 ? 1 : 0;// 把整型数组转成字符串(此处先 push 数字,再统一加 '0' 转为字符)string ansString;while(tmp < sizeNum1 + sizeNum2){ansString.push_back(ansArr[tmp++]);}// 把每个数字 + '0' 变成对应字符for(auto& c : ansString){c += '0';}return ansString;}
};

4. 字符串中的第一个唯一字符

字符串中的第一个唯一字符

思路一

只有26个小写字母,那么就可以两遍线性扫描:

  • 第一遍:记录每个字母出现的次数
  • 第二遍:找第一个只出现一次的字母并返回索引

代码实现:

class Solution {
public:int firstUniqChar(string s) {array<int,26> cnt = {0};// 遍历整个字符串,把每个字符出现次数 +1。for(char c : s){cnt[c - 'a']++;}// 从左到右再次遍历,遇到第一个出现次数为 1 的位置,直接返回其下标for (int i = 0; i < s.size(); ++i) {if (cnt[s[i] - 'a'] == 1) {return i;}}// 否则 返回-1return -1;}
};

思路二

用队列控制,一遍线性扫描即可,一边遍历一边:
1)更新该字符的计数;
2)把当前下标入队;
3)不断弹出队首中计数>1的下标,
保证队首始终是当前最左且仍唯一的候选。
遍历结束后,队首即答案(队空则 -1)

代码实现:

class Solution {
public:int firstUniqChar(string s) {array<int,26> cnt = {0};queue<int> q;for(int i = 0; i < s.size(); i++){// 记录每个字母出现次数cnt[s[i] - 'a']++;// 当前索引入队列q.push(i);// 队列不为空才能出队// 不断弹出队首,直到://   1) 队列空了(说明当前没有唯一字符),或者//   2) 队首对应字符的计数 == 1(它就是当前最靠左仍唯一的)while(!q.empty() && cnt[ s[q.front()] - 'a'] > 1){q.pop();}}// 循环体结束时:// 如果 q 非空,q.front() 就是到目前为止最左的唯一字符下标// 如果 q 为空,说明目前没有唯一字符return q.empty() ? -1 : q.front();}
};

5. 验证回文串

验证回文串
思路:
双指针,设 left=0, right=n-1,跳过两端非字母数字字符。将两端字符统一大小写(或统一映射)后比较;若不等返回 false,相等则 left++, right-- 继续,循环结束返回 true
代码实现:

class Solution {
public:// 判断是否为字母或数字static inline bool isAlnum(char c) {return (c >= '0' && c <= '9') ||(c >= 'A' && c <= 'Z') ||(c >= 'a' && c <= 'z');}// A-Z 映射到 a-zstatic inline char toLower(char c) {if (c >= 'A' && c <= 'Z') return  c + ('a' - 'A'); return c;}bool isPalindrome(string s) {int left = 0, right = (int)s.size() - 1;  while (left < right) {// 跳过左侧非字母数字while (left < right && !isAlnum(s[left])) ++left;// 跳过右侧非字母数字while (left < right && !isAlnum(s[right])) --right;// 统一成小写再比较char a = toLower(s[left]);char b = toLower(s[right]);if (a != b) return false;++left;--right;}return true;}
};
http://www.dtcms.com/a/499218.html

相关文章:

  • 分治算法-归并排序专题:从性能优化到索引数组的突破
  • iis怎么做IP网站有没有专门做数据分析的网站
  • 如何用 Docker Compose 管理多个容器
  • 《C++ STL 基础入门》教案
  • 基于对数灰关联度的IOWGA算子最优组合预测模型
  • VGW 技术解析:构建 Windows 平台的虚拟路由网关中枢
  • 内容安全优化:基于Redis实现分级反爬虫策略
  • 生成式设计案例:MG AEC利用Autodesk AEC Collection推进可持续建筑设计
  • 物流网站源代码修改wordpress后台文字
  • 【HTML】网络数据是如何渲染成HTML网页页面显示的
  • 做门图网站产品品牌推广公司
  • linux学习笔记(38)mysql索引详解
  • M1安装RocketMQ消息队列
  • 广西壮族自治区住房和城乡建设厅网站网站内页制作
  • PDFium导出pdf 图像
  • C++11标准 上 (万字解析)
  • Java基础语法—字面量、变量详解、存储数据原理
  • 手工视频制作网站移动网站建设初学视频教程
  • 【shell】每日shell练习(系统服务状态监控/系统性能瓶颈分析)
  • Swift 下标脚本
  • Spring Boot 3零基础教程,WEB 开发 默认页签图标 Favicon 笔记28
  • php 网站部署杭州企业自助建站系统
  • IntelliJ IDEA 2023中为 Spring Boot 项目添加注释模板
  • Java Web安全防护:SQL注入、XSS攻击的预防与处理
  • leetcode 912.排序数组
  • 个人网站可以做商城吗seo三人行网站
  • 第3讲:Go垃圾回收机制与性能优化
  • Mac 桌面动态壁纸软件|Live Wallpaper 4K Pro v19.7 安装包使用教程(附安装包)
  • 简易网站开发网站建设的各个环节
  • 用 Selenium 搞定动态网页:模拟点击、滚动、登录全流程