✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛)
🌈 个人Motto:他强任他强,清风拂山冈!
🔥 所属专栏:C++深入学习笔记
💫 欢迎来到我的学习笔记!
一、917.仅仅反转字母
1.1 题目描述
给你一个字符串s
,根据下述规则反转字符串:
- 所有非英文字母保留在原有位置。
- 所有英文字母(小写或大写)位置反转。
返回反转后的s
。
示例 1:
输入:s = "ab-cd"
输出:"dc-ba"
示例 2:
输入:s = "a-bC-dEf-ghIj"
输出:"j-Ih-gfE-dCba"
示例 3:
输入:s = "Test1ng-Leet=code-Q!"
输出:"Qedo1ct-eeLg=ntse-T!"
● 1 <= s.length <= 100
● s
仅由 ASCII 值在范围 [33, 122] 的字符组成
● s
不含 '\"'
或 '\\'
只反转字母,不反转特殊符号,相当于忽略掉特殊字符,首尾交换。
1.2 分析过程
想法一:使用迭代器:
- 一个正向迭代器指向开始位置;
- 一个反向迭代器指向结束位置;
- 缺点:正向迭代器与反向迭代器不能做比较,因为类型不相同。类型如下:
cout << "string::iterator" << typeid(string::iterator).name() << endl;
cout << "string::reverse_iterator" << typeid(string::reverse_iterator).name() << endl;
//string::iteratorclass std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > >
//string::reverse_iteratorclass std::reverse_iterator<class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > > >
想法二:使用两个正向迭代器即使用下标;不能使用范围for,因为底层就是迭代器。
- 避免一个字母都没有的情况:加上一个
left < right
条件; - 避免死循环不懂的情况:
swap(s[left++], s[right--]);
交换以后继续往两边走。
1.3 代码解答
class Solution
{
public:
// 判断字符是不是字母
bool isLetter(char ch)
{
if (ch >= 'a' && ch <= 'z')
return true;
else if (ch >= 'A' && ch <= 'Z')
return true;
else
return false;
}
// 使用下标进行遍历:
string reverseOnlyLetters(string s)
{
int left = 0;
int right = s.size() - 1;// size表示最后一个字符串的下一个位置
while (left < right)
{
while (left < right && !isLetter(s[left]))// left < right条件可以
{
++left;// 往右边走
}
while (left < right && !isLetter(s[right]))
{
--right;// 往左边走
}
swap(s[left++], s[right--]);// 交换,为什么++、--?避免死循环,不往后走,因此交换完后,left继续往右、right继续往左!否则他们两个就不动了
}
return s;
}
};
二、387.字符串中第一个唯一字符
7月20日3:02:00
2.1 题目描述
给定一个字符串 s
,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1
。
示例 1:
输入: s = "leetcode"
输出: 0
示例 2:
输入: s = "loveleetcode"
输出: 2
示例 3:
输入: s = "aabb"
输出: -1
提示:
● 1 <= s.length <= 105
● s
只包含小写字母
2.2 分析过程以及代码实现
- 方法一
- 时间复杂度:
O(N<sup>2</sup>)
;(不优) - 每个字符都与其他字符对比一遍。
- 方法二:计数排序
- 时间复杂度:
O(N)
; - 统计每个字符出现的次数,返回只第一个只出现一次的字符的索引(即下标)。
- 实现:
class Solution
{
public:
int firstUniqChar(string s)
{
// 使用相对映射的方法:进行计数排序
int count[26] = { 0 };
// 统计次数
for (auto ch : s)// 使用范围for进行遍历
{
count[ch - 'a']++;// ????
// 这里是要统计字符串s中的字符到26个小写字母里面去,有多少个字符,26个字母中就会出现对应的次数
}
// 这里就是用下标访问,而不是范围for
for (size_t i = 0; i < s.size(); ++i)
{
if (count[s[i] - 'a'] == 1)// 说明它只出现了一次,而且是第一个只出现一次的字符
return i;
}
return -1;// 说明s字符串中的都是重复出现的
}
};
三、415.字符串相加
7月20日3:10:00
3.1 题目描述
给定两个字符串形式的非负整数 num1
和num2
,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger
), 也不能直接将输入的字符串转换为整数形式。
示例 1:
输入:num1 = "11", num2 = "123"
输出:"134"
示例 2:
输入:num1 = "456", num2 = "77"
输出:"533"
示例 3:
输入:num1 = "0", num2 = "0"
输出:"0"
提示:
● 1 <= num1.length, num2.length <= 104
● num1 和num2 都只包含数字 0-9
● num1 和num2 都不包含任何前导零
3.2 分析过程
想法一:error
- 当整型数据很大时,即使是
long long
类型也可能存储不下,就会使用字符串进行存储、运算。然后使用字符串模拟四则运算,这就是所谓的“大数运算”。本题模拟的是加法运算。 - 字符串转换成整型:
stoi
;各种类型转换成字符串:to_string
- 但是这种方法不行,转换后数据不仅超出表示范围,而且还会丢失!
想法二:right
- 字符串中的字符倒着取,从末尾开始进行运算;
- 只要内存足够,字符串存储在内存中是一定能存储下的;(例如:整数长度100万,也就是内存空间大约1MB)
- 字符串要两个都结束了才是结束,短字符串结束时长字符串是需要继续的,例如:
"9999999999999999" + "1"
,它不断地都有进位的,不能直接将前几位字符数据拷贝下来。
class Solution {
public:
string addStrings(string num1, string num2)
{
string str;// 得到的最终结果存储在这里
int end1 = num1.size() - 1, end2 = num2.size() - 1;
// 进位
int next = 0;
while (end1 >= 0 || end2 >= 0)// 两个都要结束
{
int val1 = end1 >= 0 ? num1[end1] - '0' : 0;
int val2 = end2 >= 0 ? num2[end2] - '0' : 0;
end1--;
end2--;
int ret = val1 + val2 + next;// 加上进位
next = ret / 10;// 记下进位数:ret大于10,记下1;ret小于10,记下0
ret = ret % 10;
// 将最终的结果进行头插到str中:(不太好)
// 不断头插n次时,时间复杂度就已经O(N)了
str.insert(str.begin(), '0' + ret);
}
if (next == 1)
str.insert(str.begin(), '1');
return str;
}
};
3.3 代码实现
写法一:使用头插
class Solution {
public:
string addStrings(string num1, string num2)
{
string str;// 得到的最终结果存储在这里
int end1 = num1.size() - 1, end2 = num2.size() - 1;
// 进位
int next = 0;
while (end1 >= 0 || end2 >= 0)// 两个都要结束
{
int val1 = end1 >= 0 ? num1[end1] - '0' : 0;
int val2 = end2 >= 0 ? num2[end2] - '0' : 0;
end1--;
end2--;
int ret = val1 + val2 + next;// 加上进位
next = ret / 10;// 记下进位数:ret大于10,记下1;ret小于10,记下0
ret = ret % 10;
// 将最终的结果进行头插到str中:(不太好)
// 不断头插n次时,时间复杂度就已经O(N)了
str.insert(str.begin(), '0' + ret);
}
if (next == 1)
str.insert(str.begin(), '1');
return str;
}
};
写法二:使用尾插
class Solution {
public:
string addStrings(string num1, string num2)
{
string str;// 得到的最终结果存储在这里
int end1 = num1.size() - 1, end2 = num2.size() - 1;
// 进位
int next = 0;
while (end1 >= 0 || end2 >= 0)// 两个都要结束
{
int val1 = end1 >= 0 ? num1[end1] - '0' : 0;
int val2 = end2 >= 0 ? num2[end2] - '0' : 0;
int ret = val1 + val2 + next;// 加上进位
next = ret / 10;// 记下进位数:ret大于10,记下1;ret小于10,记下0
ret = ret % 10;
str += ('0' + ret);// 尾插,这里是operator+=的一种用法,O(N)
}
if (next == 1)
str += '1';// 结果是倒着的,就需要进行逆置:即使用算法库里面的reverse,但是要求是双向迭代器
reverse(str.begin(), str.end());
return str;
}
};