个人主页:@Sherry的成长之路
学习社区:Sherry的成长之路(个人社区)
专栏链接:C++初阶
长路漫漫浩浩,万事皆有期待
上一篇博客:C++初阶】string类常见题目详解(一)—— 仅仅反转字母、字符串中的第一个唯一字母、字符串最后一个单词的长度、验证回文串、字符串相加
链接:把字符串转换成整数
题目描述:
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。数值为0或者字符串不是一个合法的数值则返回0。输入的字符串包括数字字母符号,可以为空。
示例:
输入:“+2147483647”
输出:“2147483647”
分析:
字符串当中可能包含数字、字母和其他符号,正数可以在前面放上正号,由此可以确定的是,一个字符串一个合法的数值,则除了其第一个字符可能是正号或负号以外,其他字符都应该是数字字符。
解题思路:
1、先判断除第一个字符以外的字符,若是其中出现了非数字字符的字符,则该字符串非法,否则我们计算出其转换为整数后的数值。
2、再判断第一个字符,若是正号,则当前整数的正值;若是负号,则返回当前整数的负值;若还是数字字符,则再次更新当前整数的数值并返回;若第一个字符不属于这三种情况,则说明该字符串还是非法的。
代码:
class Solution {
public:
int StrToInt(string str) {
if (str.size() == 0) //空字符串,返回0
return 0;
int start = 0; //头指针指向第一个字符
int end = str.size() - 1; //尾指针指向最后一个字符
int ret = 0; //字符串转换为整数后的结果
int i = 1; //标识十进制当前位的权重(当前为个位,权重为1)
while (start < end) //判断第一个字符之后的字符
{
if (str[end] < '0' || str[end] > '9') //若不是数字,则非法
{
return 0;
}
ret += i*(str[end] - '0'); //ret更新
i *= 10; //下一位权重增大十倍
end--; //继续判断
}
//判断第一个字符
if (str[end] >= '0' && str[end] <= '9') //是数字
return ret + i*(str[end] - '0'); //再次更新ret并返回正值
if (str[end] == '+') //标识正数
return ret; //返回正值
if (str[end] == '-') //标识负数
return -ret; //返回负值
return 0; //第一个字符不是数字、+、-当中的一个,则还是非法
}
};
链接:344. 反转字符串
题目描述:
编写一个函数,其作用是将输入的字符串反转过来,你必须原地修改字符串,即使用O(1)的额外空间解决这一问题。你可以假设数组中的所有字符都是ASCII码表中的可打印字符。
示例:
输入:[ ‘h’, ‘e’, ‘l’, ‘l’, ‘o’ ]
输出:[ ‘o’, ‘l’, ‘l’, ‘e’, ‘h’ ]
思路:
这是一道比较简单的题目,尤其是用C++来做,我们可以使用下标的方式交换vector容器当中的数据,也可以使用逆置函数reverse对指定迭代器区间的数据进行逆置。
代码:
1.使用下标进行交换:
class Solution {
public:
void reverseString(vector<char>& s) {
//使用下标进行交换
int left = 0, right = s.size() - 1;
while (left < right)
{
swap(s[left], s[right]);
left++;
right--;
}
}
};
2.使用逆置函数进行逆置:
class Solution {
public:
void reverseString(vector<char>& s) {
//使用逆置函数进行逆置
reverse(s.begin(), s.end());
}
};
链接:541. 反转字符串 II
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例 :
输入:s = “abcdefg”, k = 2
输出:“bacdfeg”
思路:
这道题是反转字符串的进阶版,看题目可能有点难理解,我们来分析一下。
其实在遍历字符串的过程中,只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。因为要找的也就是每 2 * k 区间的起点,这样写,程序会高效很多。
所以当需要固定规律一段一段去处理字符串的时候,可以在 for 循环的表达式上进行尝试。
这里反转的逻辑可以直接使用 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;
}
};
链接:557. 反转字符串中的单词 III
给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例 1:
输入:s = “Let’s take LeetCode contest”
输出:“s’teL ekat edoCteeL tsetnoc”
解题思路
此题也可以直接在原字符串上进行操作,避免额外的空间开销。
当找到一个单词的时候,我们交换字符串第一个字符与倒数第一个字符,随后交换第二个字符与倒数第二个字符……如此反复,就可以在原空间上翻转单词。
这样,我们就完成了对一个单词的反转,我们用个 while 循环完成对整个句子里面单词的反转,而循环结束的条件就是 i < s.length()
代码:
class Solution {
public:
string reverseWords(string s) {
int len = s.length();
int i = 0;
while (i < len) {
int start = i;
// 1.找到第一个单词,遇到空格就停止
while (i < len && s[i] != ' ') {
i++;
}
// 2.反转单词的顺序
int left = start;
int right = i - 1;
while (left < right) {
swap(s[left], s[right]);
left++;
right--;
}
// 3.此时i还是指向空格的,所以继续让i++,指向下一个单词
while (i < len && s[i] == ' ') {
i++;
}
}
return s;
}
};
43. 字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意
:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
示例 1:
输入: num1 = “2”, num2 = “3”
输出: “6”
解题思路
假设 num1 = 1234,num2 = 567,则把 num1 的每一个数乘以 num2 的每一个数的结果直接写出来(不用考虑进位)
因为我们只是进行了单纯的数学运算,没有涉及任何字符串有关的操作,所以我们可以建立一个数组 ansArr 用来保存每一列相加的总和。
那么这个 ansArr 应该开多大空间呢?
两个数相乘的乘积最长是两个数长度之和,比如
999 ∗ 999 = 998001 ,为3+3=6位数
而两个数相乘的乘积最短是两个数长度之和再减1,比如
100 ∗ 100 = 10000 ,为3+3-1=5位数
我们默认开最长的空间。然后开辟数组把每一列的数都存起来,在实现代码的时候,应该怎么写才能把每列的数放到数组中正确的位置呢?
我们把 num1 的某一位称为第 i 位,把 num2 的某一位称为第 j 位,它们相乘的结果应该是位于数组的 i+j+1 的位置:
这里有一个特殊情况,比如上面的 ansArr 数组开了 7 个空间,但是第一个位置是没有值的,所以我们往 ans 里面插入数据的时候,要判断一下,假设第一个位置是 0 的话,那么就从第二个位置开始插入数据。
int index = ansArr[0] == 0 ? 1 : 0;
然后我们只需要从 ansArr 数组的最后一个位置开始,往前遍历,依次进位。
比如ansArr[6] %10= 8,此时 ansArr[6] = 8 ,然后 ,然后ansArr[5] = ansArr[5] + ansArr[6] / 10,因为ansArr[6] / 10=2,向前进 2 位,所以 ansArr[5] = 45+2=47
最后再定义一个字符串 ans,把 ansArr 的数字存进去就好了
注意
:记得要把数字转换成字符
这种方法是比较优的一种,时间复杂度分析为 O ( n ∗ m ) O(n*m)O(n∗m),n 和 m 分别是 num1 和 num2 的长度。
class Solution {
public:
string multiply(string num1, string num2) {
if (num1 == "0" || num2 == "0") {
return "0";
}
int len1 = num1.size();
int len2 = num2.size();
//两个数相乘的乘积最长是两个数长度之和,比如 999 * 999 = 998001
//两个数相乘的乘积最短是两个数长度之和再减1,比如 100 * 100 = 10000
//默认开最长
vector<int> ansArr(len1 + len2);
for (int i = len1 - 1; i >= 0; i--) {
int x = num1[i] - '0'; //把num1的字符转换为数字
for (int j = len2 - 1; j >= 0; j--) {
int y = num2[j] - '0'; //把num2的字符转换为数字
ansArr[i + j + 1] += x * y;
}
}
//进位
for (int i = len1 + len2 - 1; i > 0; i--) {
ansArr[i - 1] += ansArr[i] / 10; //前一位=后一位的进位值+自己
ansArr[i] %= 10;
}
//拼接(判断前导0的情况)
//[0] [] [] []
string ans;
int index = ansArr[0] == 0 ? 1 : 0;
while (index < len1 + len2) {
ans += (ansArr[index] + '0');
index++;
}
return ans;
}
};
今天我们比较详细地完成了string类的其他五道常见的题目。接下来,我们将进行STL中vector类的学习。希望我的文章和讲解能对大家的学习提供一些帮助。
当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~