目录
1,字符串转整型数字
2,字符串最后一个单词的长度(getline的使用)
3,仅仅反转字母
4,字符串中的第一个唯一字符(找字符串中第一个只出现一次的字符)
5,验证回文串
6,验证回文串||
7,字符串轮转
8,翻转字符串|||:翻转字符串中的单词
9,字符串中的单词反转
10,字符串相加
11,字符串相乘
1,字符串转整型数字
. - 力扣(LeetCode)
我们先来看这个:字符串 "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;
}
力扣:
. - 力扣(LeetCode)
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;elsebreak;}if(end == -1 )return 0;int pos = s.rfind(' ', end);return end - pos;}
};
3,仅仅反转字母
. - 力扣(LeetCode)
① 跳过非英文字母。并定义了一个辅助函数 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,字符串中的第一个唯一字符(找字符串中第一个只出现一次的字符)
. - 力扣(LeetCode)
此题解法和计数排序的思想有点类似。
创建一个整数数组
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,验证回文串
. - 力扣(LeetCode)
① 如果是空串就是回文。
② 跳过非字母字符以及数字
③ 都转成小写进行比较
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,验证回文串||
. - 力扣(LeetCode)
以 "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,字符串轮转
. - 力扣(LeetCode)
① 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,翻转字符串|||:翻转字符串中的单词
. - 力扣(LeetCode)
① 函数
_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,字符串中的单词反转
移除元素:
. - 力扣(LeetCode)
① 定义一个慢指针和一个快指针
。
② 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;}
};
单词反转:
. - 力扣(LeetCode)
该题目的关键就是处理空格。也是我们第一步需要做的事情。
这个题目和轮转数组类似,只不过多了一步,需要处理多余的空格。
① 处理空格
处理空格的思路就和上面的移除元素类似的,我们假设单词间隔只是一个空格,就更好理解了,如下图所示:
我们处理完空格之后,也就意味着长度要变小,所以用 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,字符串相加
. - 力扣(LeetCode)
初始化:
- 定义两个指针
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,没有就是 0if(i >= 0)--i;if(j >= 0) --j;}reverse(result.begin(),result.end()); //反转return result;}
};
11,字符串相乘
. - 力扣(LeetCode)
首先,计算两个输入字符串
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();//其中一个为 0if(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;}
};