一.字符串转换成整数


我们要对str进行处理 最后返回一个int类型
题目解析
题目说了很多的要求
简单来说就是 从读取到一个数字开始到读到不为数字的字符或者到了字符串末尾就结束 这中间的数就是要返回的 但是如果在读取之前就碰到了其他符号的话直接结束(不包括第一个加减)
在此基础上 开始可能遇到空格 遇到空格的话把空格跳过
空格之后 开始可能读取到'+'或者'-'号 也可能会有多个加减号 以读取到的第一个'+'或者'-'为准 后面的加减就当做正常的其他字符 如果读取到的是'+' 那么最终的结果就是正数 如果读取到的是'-'最终的结果就是负数
此外还有一个要求 如果最后的数字超过在32位下int的范围 需要特殊处理 大于最大值2^31-1就取这个最大值 小于-2^31 就去这个最小值 既然最终可能会越界 所有在处理过程中我们就要保证用来存储最终结果的变量不能超过int范围
过程分析

需要定义以上的变量 i用来遍历string类型的str con用来存储最终要返回的数 c用来处理最终符号的结果(最终将con的值*c) 没有加减号默认为正 max在之后对int范围解释再说

然后先对空格和最终的符号进行处理
遇到空格直接结束这一次 空格问题就解决完了
空格之后的遍历 开始遇到的可能三种情况
- +- 取遇到的第一个然后对c(最后根据此判断最终符号)进行处理 之后的+-就相当于其他字符 虽然刚开始就将c初始化为1了 但是这里还是需要'+'的判断 否则如下面这种情况 +就被当做其他字符直接返回0结束了

2.其他字符直接结束返回0
3.数字的话进入数字的处理 之后再读取到一个非数字的就会结束

之后对数字这里处理
首先读取到的数字是从左往右的顺序 所以每次加上新的一位之前将之前的*10(这样保证新加的一位在最后)
然后这里主要的问题就是int的范围 在过程中我们要保证con没有超过int的范围
对这个范围分析一下
32 位有符号整数的范围是 [−2^31,2^31−1],即 [−2,147,483,648,2,147,483,647]。
INT_MAX=2147483647 INT_MIN=-2147483648 这里先留意一下 max要比min最后一位少1
con每次更新是将之前的con乘10然后多一位数
INT_MAX/10=214768364 所以如果con大于该值了 在 *10之后一定就大于int最大值了就越界了
如果小于该值的话*10之后一定不会越界 如果等于该值就要看多的那一位数是否大于7
大于的情况 一定是越界的 要么是int最大值 要么是最小值 所以这里只需要根据符号决定是哪个就可以了
小于的情况这一次一定不会越界但是下一次就是一定越界的情况 如果还有数的情况 下一次就和大于的情况一样了 如果没有数了 接下来就是正常的根据符号觉得最终值
等于的话 看多的那一位 这里需要注意一下多出的那一位为什么这里要大于7进是一定越界
如果最终返回结果是正数 最后一位==7是刚好是不越界的情况 就是int的最大值 所以这里其实可以写7等于 对于最后一位整数的情况 >=7都可以 最后一位7 8 9都可以(8 9是必须包含的)
但是最终是负数的话 最小值最后一位是8 最后一位如果等于7的话还不是它的最大值(这里还没有算符号 ) 如下面的情况 最后一位8 9都可以 (9 是必须包含的)
所有在==int最大值/10的情况 多的那一位的值的判断必须有8和9 >7 或者>=8都可以

具体代码
cpp
class Solution {
public:
int myAtoi(string str) {
int i = 0;
int c = 1;
int con = 0;
int max = INT_MAX / 10;
for (i = 0; i < str.size(); i++)
{
if (str[i] == ' ')
{
continue;
}
else if(str[i]=='+')
{
c=1;
i++;break;
}
else if (str[i] == '-')
{
c = -1;
i++; break;
}
else if (str[i] >= '0' && str[i] <= '9')
break;
else
return 0; //处理其他字符
}
while (i < str.size())
{
if (str[i] >= '0' && str[i] <= '9')
{
if (con > max || (con==max ) && str[i] >='8') //这里的max是int最大值/10后的
return c == 1 ? INT_MAX : INT_MIN;
con = con * 10 + (str[i] - '0');
}
else
break;
i++;
}
return con * c;
}
};
二.字符串中第一个唯一字符


题目解析
题目描述很简单 就是给了一个字符串s 返回在字符串中第一个只出现一次的字符的下标位置
过程分析
这里用到类似计数排序的方法 字符串s里面的数据就是52个英文字母 把各个字母出现的次数用数组来存起来
在ASCIl码中 一个字符减去它本身就是0 而A~Z又是连续的 那么每一个都减去'A' 就是0~25的数字 而小写字母的ASCII比大写的要大32 那么小写的-'A'的范围就是32~57
所以只需要一个int类型的数组开足够的空间 每次读到的是哪个字母 对应的位置就++ 遍历结束之后 对应位置==1的就是只出现一次的字母
之后再遍历字符串s 第一个==1的就是最终要返回的 返回的是对应下标 如果遍历结束都没有 就没有符合要求的字符 返回-1
具体代码
cpp
class Solution {
public:
int firstUniqChar(string s) {
int arr[100] = { 0 };
for (char x : s)
{
arr[x - 'A']++;
}
for (int i = 0; i < s.size(); i++)
{
if( arr[s[i] - 'A'] == 1)
return i;
}
return -1;
}
};
三.反转字符串的单词lll


题目解析
字符串里面可能有多个单词 要把每一个单独的单词给翻转一下
注意单独的单词中可能存在' ': 等这些也要反转 每个单词之间以一个空格间隔开头和结尾是没有额外空格的
过程分析
这里是要把每一个单独的单词分别给反转 而各个单词间以一个空格分隔 最后一个字符之后又一定没有空格 所以每一个单词的后一个位置要么是一个空格 要么是到了最后
所以我们可以用双指针的方法 一个指针left指向要反转单词的开头 一个指针right指向要反转单词末尾的后一个位置 然后用reserve迭代器的方式来进行反转单个单词
因为开始没有空格 所以left下标0的位置 就是第一个单词的第一个第一个字母
right要指向的是单词末尾的后一个位置 right刚开始指向第二个位置 从下标1开始如果指向的位置不是空格或者right为n也就是字符串末尾的后一个位置就继续++
然后反转对应区间的单词 之后right++ left指向right的位置 因为上一次right指向的是空或者最后一个位置 对于第一种情况right++后的位置就是下一个单词第一个字母的位置 对于第二组情况left更新之后 下一次循环条件判断时候就不会进去
具体代码
cpp
class Solution {
public:
string reverseWords(string s) {
int n = s.size();
int left = 0, right = 1;
while (left < n)
{
while (s[right]!=' '&& right != n)
{
right++;
}
reverse(s.begin() + left, s.begin() + right);
right++;
left=right;
}
return s;
}
};
四.反转字符串


直接用一个reverse迭代器的方式就好了 或者就双指针的方法
cpp
class Solution {
public:
void reverseString(vector<char>& s) {
int n=s.size();
int j=n-1;
int i=0;
while(i<j)
{
char k=s[i];
s[i++]=s[j];
s[j--]=k;
}
}
};
五. 字符串最后一个单词的长度

题目解析
可能有多个单词 至少一个单词 要算最后一个单词的长度 这里还要求自己写输入的方式
过程分析
这里的输入不能用cin 因为遇到空格就直接结束这次的读取了 所以这里用getline(读到换行才会结束 或者自己手动选择结束的值)
要算最后一个单词的长度 那我们只需要一直更新读到单词的长度就可以
每个单词之间一定存在着空格 我们可以选择遇到空格就将读取到的长度清零 最后一个单词之前一定有空格读最后一个单词之前一定会清零 这样就将n里面存的最后一个单词的长度直到遇到'\0'结束
具体代码
cpp
#include <iostream>
using namespace std;
int main() {
string s;
int n = 0;
getline(cin,s);
int i = 0;
while (s[i] != '\0')
{
if ((s[i] > 'a' && s[i] < 'z') || (s[i] > 'A' && s[i] < 'Z'))
n++;
else if (s[i] == ' ')
{
n = 0;
}
i++;
}
cout<<n;
}
六.验证回文串

题目解析
就是正常的回文判断 但是如果有大写的字符需要将其转换为小写并将非数字和字母的字符给移除之后再进行判断
过程分析
先说两种思路
1.直接用双指针 一个在最左一个最右的方法 在对单个字符处理过程中对大写转小写和移除非字母和数字字符进行处理
2.先进行大写转小写和移除非字母和数字字符 剩下的字符串就只需要进行最基本的回文判断了 这时候用双指针或者与它反转之后进行比较的方法来验证都可以
第一种
以下操作让left和right指向的是数字或者字母 这里的isalnum就是判断是不是字母或者数字的函数是的话返回1(true) 要注意每一次移动之前还是需要left<right的判断 防止越界

下一步将大写转换为小写 这也可以直接用tolower(是大写字母就转换为小写字母 不是不做处理)

这样剩下的不是小写字母就是就是数字 就可以直接进行判断是否相等

cpp
class Solution {
public:
bool isPalindrome(string s) {
int left=0,right=s.size()-1;
while(left<right)
{
while(left<right&&!isalnum(s[left])) left++;
while(left<right&&!isalnum(s[right])) right--; //先让指向字符或者数字
if(s[left]>='A'&&s[left]<='Z')
s[left]+=32;
if(s[right]>='A'&&s[right]<='Z') //将大写先转换为小写
s[right]+=32;
if(isalnum(s[left])&&isalnum(s[right])) //判断是否相等
{
if(s[left]!=s[right])
return false;
}
left++; //这里不管是if成立或者不成立都需要left++right--分别指向下一个位置
right--;
}
return true;
}
};
第二种
直接用范围for的方式对里面的每一个字符处理 如果是字母或者字符的话 经过tolower(大写转小写)之后加到ss字符串中 遍历完之后 ss中的就只有小写字母和数字了 然后这时候用看它和反转后是否相等或者双指针都可以
cpp
class Solution {
public:
bool isPalindrome(string s) {
string ss;
for(char x:s)
{
if(isalnum(x))
ss+=tolower(x);
}
int left=0,right=ss.size()-1;
string ss2=ss;
reverse(ss.begin(),ss.end());
return ss2==ss;
}
};
七.反转字符串II
题目解析
其实很简单 但是它这个描述容易理解错了
每遇到2k个字符就将前k个反转剩下的k个不做处理 一直到最后不够2k的字符了 此时>=k个就将前k个反转 剩下的不处理 小于k个都反转
经过简化通俗点的说就是每隔k个反转k个,末尾不够k个时全部反转
反转很简单用reverse传迭代器就可以
按照它描述的写就是以下
具体代码
cpp
class Solution {
public:
string reverseStr(string s, int k) {
int n=s.size();
int c=0;
while(n>2*k)
{
reverse(s.begin()+c,s.begin()+k+c);
n-=2*k;
c+=2*k;
}
if(n>=k)
{
reverse(s.begin()+c,s.begin()+k+c) ;
}
else
reverse(s.begin()+c,s.begin()+n+c) ;
return s;
}
};
按照简化后的理解就是如下
cpp
class Solution {
public:
string reverseStr(string s, int k) {
int n = s.size();
for (int i = 0; i < n; i += 2 * k)
{
reverse(s.begin() + i, s.begin() + min(i + k, n));
}
return s;
}
};
八.字符串相加

题目解析
给了两个字符串 其实就是按照加法竖式计算的方式用字符串来模拟实现
这是很有意义的 int来计算的话 如果数大一些的话就可能会越界了 计算的位数很有限 但是对于字符串来说能存很多很多
过程分析
其实就是模拟竖式加法的方式 所以我们要考虑进位的情况 每一次得到某一位的值给了一个新的字符串s2 在最后再把它反转 里面存的就是最终的结果
先创立两个int类型的 size1 size2分别指向两个字符串的末尾位置 如果两个都大于等于0(==0就是为此时字符串的最高位)就继续
计算两个字符串同位的和再加上jw 刚开始个位虽然没有jw 但是初始化jw为0所以也不会影响 然后更新jw为add/10后的结果 然后再把该位应该的值(也就是/10后的结果 大于十的取了个位 小于十的就是本身)给了s2
这里注意字符串里面存的字符是ASCII的形式 计算的时候要减去'0'转换为int类型来计算 存的时候需要再加上'0'转换
结束的条件是某一个字符串加完了 但是此时可能还有一个没有加完 对于没有加完的再进行它那里的处理
然后最后还需要判断此时的jw是否为1 最后一次结束之后 可能还有一个进位没有处理 需要额外在加一个1 如99+10 在处理完十位之后就结束了 但是这时候还有一个百位的进位没有处理 还需要加个1 之后反转后的结果就是要求的
具体代码
cpp
class Solution {
public:
string addStrings(string num1, string num2) {
int size1 = num1.size() - 1;
int size2 = num2.size() - 1;
string s2;
int add;
int jw = 0;
while (size1 >= 0 && size2 >= 0) {
add = num1[size1--] - '0' + num2[size2--] - '0' + jw;
jw = add / 10;
s2 += add % 10 + '0';
}
while (size1 >= 0) {
add = num1[size1--] - '0' + jw;
jw = add / 10;
s2 += add % 10 + '0';
}
while (size2 >= 0) {
add = num2[size2--] - '0' + jw;
jw = add / 10;
add %= 10;
s2 += add % 10 + '0';
}
if (jw == 1)
s2 += jw + '0';
reverse(s2.begin(), s2.end());
return s2;
}
};
九.字符串相乘 
题目解析
类似上一个相加 这里相当于要模拟竖式乘法
过程分析

以上述为例 同样的开始先定义两个int类型的end1 end2分别指向每一个字符串末尾的字符也就是此时end1指向3 end2指向6 先固定此时的end1 改变end2 先计算3*456的结果 计算过程类似于加法那里 用ss来存 还是注意最后一次的进位 然后反转ss

之后还需要计算第一个字符串的十位和第二个字符串的结果 百位和第二个字符串结果 最后完了之后要把这三个字符串给加起来 就用到了上一题的字符串加法 加起来的结果用string类型的s2来接收
在后面第一个字符串的十位和第二个字符串的计算中最后其实要有一个0 百位和第二个字符串的计算结果要有两个0 所以可以创建一个n初始为0 每一次结束之后就++ 然后根据n来决定 每一次计算的ss提前先加多少个0(最后反转之后就相当于在末尾添加) 这里用nn=n 因为n不能改变 不用nn 在处理0过程就把n给改变了

开始如果有一个为0的情况直接返回"0" 要求的是string类型 这里会隐式转换为string类型
具体代码
cpp
class Solution {
public:
string addStrings(string num1, string num2) { //字符串相加
int size1 = num1.size() - 1;
int size2 = num2.size() - 1;
string s2;
int add;
int jw = 0;
while (size1 >= 0 && size2 >= 0) {
add = num1[size1--] - '0' + num2[size2--] - '0' + jw;
jw = add / 10;
s2 += add % 10 + '0';
}
while (size1 >= 0) {
add = num1[size1--] - '0' + jw;
jw = add / 10;
s2 += add % 10 + '0';
}
while (size2 >= 0) {
add = num2[size2--] - '0' + jw;
jw = add / 10;
add %= 10;
s2 += add % 10 + '0';
}
if (jw == 1)
s2 += jw + '0';
reverse(s2.begin(), s2.end());
return s2;
}
string multiply(string num1, string num2) {
if (num1 == "0" || num2 == "0")
return "0";
int end1 = num1.size() - 1, end2 = num2.size() - 1;
int _end2 = end2; //先存一下 之后计算完一轮之后 end2还要从最后一位开始
string s2; //存最终结果
int n = 0; //来处理ss提起加多少个0
while (end1 >= 0) {
string ss; //每一轮之后要把ss清空 否则还存着上一轮的数据
int nn = n;
while (nn--) {
ss += '0';
}
end2 = _end2; //每一轮之前要更新为最后一个位置
int jw = 0;
while (end2 >= 0) {
int add = (num1[end1] - '0') * (num2[end2] - '0') + jw;
ss += (add % 10) + '0';
jw = add / 10;
end2--;
}
if (jw > 0)
ss += jw + '0';
reverse(ss.begin(), ss.end());
s2 = addStrings(s2, ss);
end1--;
n++;
}
return s2;
}
};