目录
4,字符串中的第一个唯一字符(找字符串中第一个只出现一次的字符)
1,字符串转整型数字
我们先来看这个:字符串 "12345" 转成整数 12345 怎么转呢?
第一次循环时,
val
初始为 0,乘以 10 还是 0,加上'1' - '0'
即 1,val
变为 1。第二次循环,
val
为 1,乘以 10 得到 10,加上'2' - '0'
即 2,val
变为 12,以此类推,最终得到 12345 并输出。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("12345");
int val = 0;
for (size_t i = 0; i < s.size(); ++i)
{
val *= 10;
val += s[i] - '0';
}
cout << val << endl;
return 0;
}
有了该基础,做这个题目就很轻松了
请你来实现一个
myAtoi(string s)
函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的atoi
函数)。函数
myAtoi(string s)
的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为
0
。必要时更改符号(从步骤 2 开始)。- 如果整数数超过 32 位有符号整数范围
[−231, 231 − 1]
,需要截断这个整数,使其保持在这个范围内。具体来说,小于−231
的整数应该被固定为−231
,大于231 − 1
的整数应该被固定为231 − 1
。- 返回整数作为最终结果。
由题意得:
① 我们先去掉前导空格,就是把前面的空格都去掉。
② 判断该字符串最后的正负
③字符串转整数 和上的转的逻辑类似,只不过多了一步需要检查溢出,因为题目说是一个 32 位有符号整数
。if (val > (INT_MAX / 10) || (val == (INT_MAX / 10) && digit > 7))
:因为INT_MAX
是 2147483647,如果当前累加的结果val
已经大于INT_MAX / 10
,或者val
等于INT_MAX / 10
并且即将累加的数字digit
大于 7(因为2147483647
的十位是 4,下一位最大只能是 7),那么就会发生溢出,此时根据正负号返回INT_MAX
或INT_MIN
。
class Solution {
public:
int myAtoi(string str)
{
//去掉前导空格
int i = 0;
while(i < str.size() && str[i] == ' ')
{
++i;
}
//判断正负
int flag = 1;
if(i < str.size() && (str[i] == '-'|| str[i] == '+') )
{
flag = (str[i] == '-') ? -1 : 1;
++i;
}
//字符数转整数
int val = 0;
int digit = 0;
for(; i < str.size() && isdigit(str[i]); ++i)
{
digit = str[i] - '0';
//检查是否溢出
if (val > (INT_MAX / 10) ||
(val == (INT_MAX / 10) && digit > 7))
{
return (flag == 1)? INT_MAX : INT_MIN;
}
val *= 10;
val += digit;
}
return val * flag;
}
};
2,字符串最后一个单词的长度(getline的使用)
牛客:
std::getline(std::cin, str)
会从标准输入(通常是键盘)读取一行文本,并将其存储到
str
字符串中。而 C++ 中的 cin 去读取的话,遇到空格或回车就会结束
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
getline(cin,s);
size_t pos = s.rfind(' ');
cout << s.size() - pos -1 << endl;
}
力扣:
int end = s.size() - 1;
:初始化一个索引end
,使其指向字符串s
的最后一个字符。接下来的
while
循环从字符串的末尾开始向前移动,跳过所有的空格字符。如果当前字符是空格,就将end
减 1;如果遇到非空格字符,就跳出循环。如果经过上述循环后
end
变成了 -1,说明整个字符串都是空格,返回 0 。
int pos = s.rfind(' ', end);
:使用rfind
函数在字符串中从位置end
往前查找第一个空格的位置,并将其索引存储在pos
中。最后,通过
end - pos
计算出最后一个单词的长度并返回。
class Solution {
public:
int lengthOfLastWord(string s)
{
int end = s.size() - 1;
while(end >= 0)
{
if(s[end] == ' ')
--end;
else
break;
}
if(end == -1 )
return 0;
int pos = s.rfind(' ', end);
return end - pos;
}
};
3,仅仅反转字母
① 跳过非英文字母。并定义了一个辅助函数 isenglish, 用于判断一个字符是否为英文字母(包括小写和大写)。
② 遇到英文字母就进行交换。
class Solution {
public:
bool isenglish(char c)
{
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
{
return true;
}
return false;
}
string reverseOnlyLetters(string s)
{
//
int start = 0;
int end = s.size() - 1;
while(start < end)
{
//跳过非英文字母
while(start < end && !isenglish(s[start]))
{
++start;
}
while(start < end && !isenglish(s[end]))
{
--end;
}
swap(s[start],s[end]);
++start;
--end;
}
return s;
}
};
4,字符串中的第一个唯一字符(找字符串中第一个只出现一次的字符)
此题解法和计数排序的思想有点类似。
创建一个整数数组
count
,大小为 26,用于统计每个小写字母出现的次数。通过遍历字符串s
,将每个字符ch
对应的计数加 1, count[ch - 'a']++
利用字符与 'a' 的差值作为索引来对应相应的字母)。然后,再次遍历字符串
s
,对于每个字符,如果其在count
数组中的计数为 1,就返回其索引i
。如果遍历完整个字符串都没有找到只出现一次的字符,就返回 -1。
class Solution {
public:
int firstUniqChar(string s)
{
int count[26] = { 0 };
for(auto ch : s)
{
count[ch - 'a']++;
}
for(size_t i = 0; i < s. size(); ++i)
{
if(count[s[i] - 'a'] == 1)
{
return i;
}
}
return -1;
}
};
5,验证回文串
① 如果是空串就是回文。
② 跳过非字母字符以及数字
③ 都转成小写进行比较
class Solution {
public:
bool iselglishchar(char ch)
{
if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|| (ch >= '0' && ch <= '9'))
{
return true;
}
return false;
}
bool isPalindrome(string s)
{
if(s == " " )
{
return true;
}
int start = 0;
int end = s.size() - 1;
while(start < end)
{
//跳过非字母字符
while(start < end && !iselglishchar(s[start]))
{
++start;
}
while(start < end && !iselglishchar(s[end]))
{
--end;
}
if(tolower(s[start]) != tolower(s[end]))
{
return false;
}
++start;
--end;
}
return true;
}
};
6,验证回文串||
以 "abdda" 这个串为例,此时 i 指向'b',j 指向'd',发现不对了。
但是有一次删除的机会,我们自己写几个情况其实就能发现,此时子串范围为(i+1, j)
或 (i, j-1) 的两个子串只要有任意一个是回文串,则结果就是回文串,否则就不是。
当 "ab" 我们随便删除一个都是 true,所以"ab" 删除之后,返回的是 true。
class Solution {
public:
bool isVild(string s, int start, int end)
{
while(start < end)
{
if(s[start] != s[end])
{
return false;
}
++start;
--end;
}
return true;
}
bool validPalindrome(string s)
{
int start = 0;
int end = s.size() - 1;
while(start < end)
{
if(s[start] != s[end])
{
return isVild(s,start+1, end) || isVild(s,start,end-1);
// (s,1,1) // (s.0,0)
}
++start;
--end;
}
return true;
}
};
7,字符串轮转
① s1.size() == s2.size()
:首先检查两个字符串的长度是否相等。如果长度不相等,那么它们肯定不是翻转关系,直接返回false
。
② (s2 + s2).find(s1)!= string::npos
:如果长度相等,那么将s2
自身连接起来得到s2 + s2
。然后在这个新字符串中查找s1
。如果能找到(即find
函数的返回值不为string::npos
,表示未找到的特殊值),说明s1
可以通过某种方式在s2
的翻转中得到,返回true
;如果找不到,返回false
。
class Solution {
public:
bool isFlipedString(string s1, string s2)
{
return s1.size() == s2.size() && (s2 + s2).find(s1) != string::npos;
}
};
8,翻转字符串|||:翻转字符串中的单词
① 函数
_reverseWords
用于反转指定范围内的字符顺序。通过一个循环,不断交换起始位置start
和结束位置end
的字符,实现反转。② 函数
reverseWords
首先定义一个起始位置start
,然后遍历输入字符串s
。当遇到空格时,调用_reverseWords
函数反转从start
到当前空格前一位(即i - 1
)的单词。然后更新start
为空格后的位置(即i + 1
)。③ 遍历结束后,再单独处理最后一个单词,因为它后面没有空格来触发反转。
④ 最终返回反转单词顺序后的字符串
s
。比如说,对于输入字符串
"hello world"
,首先会反转"hello"
为"olleh"
,然后更新start
,接着反转"world"
为"dlrow"
,得到最终结果"olleh dlrow"
。
class Solution {
public:
// 反转单词
void _reverseWords(string& s, int start, int end)
{
while (start < end)
{
char tmp = s[start];
s[start++] = s[end];
s[end--] = tmp;
}
}
string reverseWords(string s)
{
int start = 0;
for (size_t i = 0; i < s.size(); ++i)
{
if (s[i] ==' ')
{
_reverseWords(s, start, i - 1);
start = i + 1;
}
}
// 处理最后一个单词
_reverseWords(s, start, s.size() - 1);
return s;
}
};
9,字符串中的单词反转
移除元素:
① 定义一个慢指针和一个快指针
。
② fast
指向我们想要获取的元素。
③ slow
指向获取快指针获取的元素所指向新的位置 ;④ 当快指针指向了需移除的元素时,快指针跳过他,因为这个元素不是我们需要的,到达下一个位置,下一个位置不是要移除的元素就是,我们需要的元素,所需元素就复制给 slow 指针。
class Solution {
public:
int removeElement(vector<int>& nums, int val)
{
int slow = 0;
for(int fast = 0; fast < nums.size(); ++fast)
{
if(nums[fast] != val)
{
nums[slow] = nums[fast];
++slow;
}
}
return slow;
}
};
单词反转:
该题目的关键就是处理空格。也是我们第一步需要做的事情。
这个题目和轮转数组类似,只不过多了一步,需要处理多余的空格。
① 处理空格
处理空格的思路就和上面的移除元素类似的,我们假设单词间隔只是一个空格,就更好理解了,如下图所示:
我们处理完空格之后,也就意味着长度要变小,所以用 resize 来调整字符串的长度。
② 每个单词反转
③ 整体单词反转
class Solution {
public:
string reverseMessage(string message)
{
//1,处理空格
int slow = 0;
for(int fast = 0; fast < message.size(); ++fast)
{
if(message[fast] != ' ')
{
if(slow != 0)
{
message[slow++] = ' '; //就算存在很多空格也会被覆盖
}
while(fast < message.size() && message[fast] != ' ')
{
message[slow++] = message[fast++];
}
}
}
message.resize(slow);
//2,单词反转
int pos = 0;
for(int i = 0; i < message.size(); ++i)
{
if(message[i] == ' ')
{
if(pos < i)
reverse(message.begin() + pos, message.begin() + i);
pos = i + 1;
}
}
//最后一个单词反转
if(pos < message.size())
{
reverse(message.begin() + pos, message.end());
}
//整体翻转
reverse(message.begin(), message.end());
return message;
}
};
10,字符串相加
初始化:
- 定义两个指针
i
和j
分别指向两个字符串num1
和num2
的末尾。- 初始化进位
cur
为 0 。- 创建一个空字符串
result
用于存储相加的结果。进入循环,只要以下三个条件之一满足就继续循环:
i
大于等于 0,即num1
还有未处理的数字。j
大于等于 0,即num2
还有未处理的数字。- 进位
cur
大于 0,表示进位了(因为最后加完) 。在循环内部:
- 如果
i
大于等于 0 ,获取num1
对应位置的数字并减去字符 '0' 的值,得到实际数字sum1
,如果i
小于 0 ,则sum1
为 0 。- 同样,如果
j
大于等于 0 ,获取num2
对应位置的数字并减去字符 '0' 的值,得到实际数字sum2
,如果j
小于 0 ,则sum2
为 0 。计算当前位的总和
sum
为sum1 + sum2 + cur
。将总和对 10 取余,加上字符 '0' 的值,添加到结果字符串
result
中。更新进位
cur
为总和除以 10 的值。如果
i
大于等于 0 ,将i
减 1 ;如果j
大于等于 0 ,将j
减 1 。循环结束后,反转结果字符串
result
。返回最终的结果字符串。
class Solution {
public:
string addStrings(string num1, string num2)
{
//倒着加,记录进位,保存数组到字符串,翻转字符串
int i = num1.size() - 1;
int j = num2.size() - 1;
int cur = 0;
string result = "";
while(i >= 0 || j >= 0 || cur > 0)
{
int sum1 = (i >= 0) ? (num1[i] - '0') : 0;
int sum2 = (j >= 0) ? (num2[j] - '0') : 0;
int sum = sum1 + sum2 + cur;
result += sum % 10 + '0'; //两个数相加的结果
cur = sum / 10; //看看是否进位 1,没有就是 0
if(i >= 0)
--i;
if(j >= 0)
--j;
}
reverse(result.begin(),result.end()); //反转
return result;
}
};
11,字符串相乘
首先,计算两个输入字符串
num1
和num2
的长度,并进行特殊情况判断。如果其中一个字符串表示的数字为 0(即长度为 1 且字符为 '0'),则直接返回 "0" 。为
num1
分配一个整数数组nums1
,并将num1
中的字符转换为对应的整数。初始化一个整数数组
sums
用于存储乘法运算的中间结果和最终求和结果,初始值都为 0 。进行乘法运算和累加:
- 对于
num2
中的每个数字(从右往左),取出当前数字multiply1
。- 对于
num1
中的每个数字(从右往左),计算multiply1
与当前数字的乘积,并累加到sums
数组中对应的位置(位置由两个数字的位置决定)。对
sums
数组进行求和处理:
- 如果某个位置的数字大于等于 10,将其除以 10 的商累加到下一位,当前位取余数。
将
sums
数组中的数字转换为字符并连接成字符串sb
。如果最后一位是 0 且是唯一的非零数字,则跳过不添加。反转字符串
sb
。释放为
nums1
和sums
分配的内存。返回最终的结果字符串
sb
。
class Solution {
public:
string multiply(string num1, string num2)
{
//1,计算字符串的长度
int len1 = num1.size();
int len2 = num2.size();
//其中一个为 0
if(len1 == 1 && num1[0] - '0' == 0 || len2 == 1 && num2[0] - '0' ==0)
{
return "0";
}
//第一个数转整数
int* nums1 = new int[len1];
for(int i = 0; i < len1; ++i)
{
num1[i] = (num1[i] - '0');
}
//累加
int* sums = new int[len1 + len2]();
for(int i = 0; i < len2; ++i)
{
int multiply1 = num2[len2 - 1 -i] - '0';
for(int j = 0; j < len1; ++j)
{
sums[i + j] += multiply1 * num1[len1 - 1 - j]; //第一波相对于一行,三行相加
}
}
//求和
string sb = "";
for(int i = 0; i < len1 + len2; ++i)
{
if(sums[i] >= 10)
{
sums[i + 1] += sums[i] / 10; //后一位加上前面的进位 +=
sums[i] = sums[i] % 10; // 第一位直接取余赋值
}
if(i == len1 + len2 -1 && sums[i] == 0) //跳过最后,如果值为 0 防止把 '0' 赋值到字符串前面
continue;
sb += sums[i] + '0';
}
reverse(sb.begin(),sb.end());
delete[] nums1;
delete[] sums;
return sb;
}
};