文章目录
2788.按照分隔符拆分字符串(字符串数组的模拟需要两次for循环)
给你一个字符串数组 words
和一个字符 separator
,请你按 separator
拆分 words
中的每个字符串。
返回一个由拆分后的新字符串组成的字符串数组,不包括空字符串 。
注意
separator
用于决定拆分发生的位置,但它不包含在结果字符串中。- 拆分可能形成两个以上的字符串。
- 结果字符串必须保持初始相同的先后顺序。
示例 1:
输入:words = ["one.two.three","four.five","six"], separator = "."
输出:["one","two","three","four","five","six"]
解释:在本示例中,我们进行下述拆分:
"one.two.three" 拆分为 "one", "two", "three"
"four.five" 拆分为 "four", "five"
"six" 拆分为 "six"
因此,结果数组为 ["one","two","three","four","five","six"] 。
示例 2:
输入:words = ["$easy$","$problem$"], separator = "$"
输出:["easy","problem"]
解释:在本示例中,我们进行下述拆分:
"$easy$" 拆分为 "easy"(不包括空字符串)
"$problem$" 拆分为 "problem"(不包括空字符串)
因此,结果数组为 ["easy","problem"] 。
示例 3:
输入:words = ["|||"], separator = "|"
输出:[]
解释:在本示例中,"|||" 的拆分结果将只包含一些空字符串,所以我们返回一个空数组 [] 。
提示:
1 <= words.length <= 100
1 <= words[i].length <= 20
words[i]
中的字符要么是小写英文字母,要么就是字符串".,|$#@"
中的字符(不包括引号)separator
是字符串".,|$#@"
中的某个字符(不包括引号)
思路
本题的思路就是模拟法,可以直接调用c++的getline库函数,getline函数也就是处理输入流字符串的分割,并把分割好的字符串存在第二个参数item中。
库函数的做法
class Solution {
public:
vector<string> splitWords(vector<string>& words, char separator) {
vector<string> result;
for (const string& word : words) {
//stringstream字符串转化为流
stringstream ss(word);
string item;
while (getline(ss, item, separator)) {
if (!item.empty()) {
result.push_back(item);
}
}
}
return result;
}
};
补充:std::getline函数
C++的getline
函数用于从输入流读取字符串。
getline
是C++标准库中的一个函数,用于从输入流中读取字符串。这个函数非常适合用来读取文本文件的内容,或者从输入设备(如键盘)获取用户输入的文本。它也可以用来从字符串流(std::stringstream
)读取字符串。
getline
函数有两种常见的用法:
std::getline(std::istream& input, std::string& str)
: 这个版本的函数从输入流input
读取一行字符串,并存储在str
中。函数会读取所有字符,直到遇到换行符('\n'
)或者文件结束符(EOF)。遇到的换行符会被消耗掉,但不会被存入str
。例如:
std::string line;
while (std::getline(std::cin, line)) {
// 处理每一行
}
std::getline(std::istream& input, std::string& str, char delimiter)
: 这个版本的函数同样从输入流input
读取字符串,但是会在遇到特定的分隔符delimiter
时停止读取,而不是仅在遇到换行符时停止。例如,你可以用逗号作为分隔符,来读取CSV文件中的一个字段:
std::string field;
while (std::getline(std::cin, field, ',')) {
// 处理每个字段
}
std::string field
是一个字符串变量,用于存储从输入流(此处是std::cin
,也就是标准输入,通常指的是键盘输入)中通过std::getline
读取的数据。
std::getline(std::cin, field, ',')
这一行代码的功能是从输入流中读取字符并存储到field
字符串中,直到遇到分隔符(此处的分隔符是逗号,
)。换句话说,它读取的是逗号分隔的一段文本。
这段代码通常用于读取CSV(Comma-Separated Values)格式的数据,也就是以逗号为分隔符的数据。每次循环,field
都会被设置为下一个逗号之前的文本。例如,如果用户输入的是apple,banana,carrot
,那么在第一次循环时,field
会被设置为apple
,在第二次循环时,field
会被设置为banana
,在第三次循环时,field
会被设置为carrot
。
"处理每个字段"这一部分的代码应该实现对field
变量的具体处理,例如打印出来,或者存储到其他数据结构中等等,这取决于你的程序需要完成的具体任务。
以上两种形式都会返回输入流,也就是说,在while
循环中。如果输入流仍然有效(也就是说,没有遇到错误,并且还没有读到文件的结束),那么getline
函数就会返回true
。如果输入流不再有效,getline
函数就会返回false
,这可以用来控制循环的结束。
模拟法
- 因为是字符串数组所以需要两层for循环来解决
- string本身就是一种容器,字符串可以直接push_back()
class Solution {
public:
vector<string> splitWordsBySeparator(vector<string>& words, char separator) {
//string的数组属于二维char数组
vector<string>result;
//建立一个字符数组来存放单个单词
string buffer;//buffer来存每个单个的单词,注意buffer本身也是数组所以可以用push_back
for(string word:words){
for(char c:word){
if(c!=separator){
buffer.push_back(c);
continue;
}
else{
if(!buffer.empty()){
result.push_back(buffer);
buffer.clear();
}
}
}
//buffer里还有最后一个,一定要在每个单词处理结束之后执行
if(!buffer.empty()){
result.push_back(buffer);
buffer.clear();
}
}
return result;
}
};
模拟注意:string也是容器
std::basic_string - cppreference.com
在C++中,std::string
类确实提供了许多类似于标准模板库(STL)容器的方法,例如push_back
、size
、empty
等等,这使得std::string
在使用方式上与其他STL容器(如std::vector
、std::list
、std::deque
等)非常相似。
然而,虽然std::string
和STL容器在使用方式上有很多相似之处,但是它并不被正式归类为STL容器。这是因为**std::string
是专门用于处理字符串的类**,提供了很多特定于字符串处理的方法和功能(比如查找子串、连接字符串等),这些在其他STL容器中并不存在。
不过,std::string
确实是STL的一部分,它被归类在序列容器(sequence container)中。其设计思想和实现方式都和其他STL组件一致,因此有时候我们也会把它看作是STL容器的一种。
总虽然std::string
并不正式地被归类为STL容器,但是它在很大程度上是遵循和实现了STL容器的接口和使用方式的。可以把std::string
当作是一个特殊的、专门用于处理字符串的STL容器,可以使用push_back,pop_back()等操作。
2789. 合并后数组中的最大元素(也是模拟,但是要想到遍历顺序)
给你一个下标从 0 开始、由正整数组成的数组 nums
。
你可以在数组上执行下述操作 任意 次:
- 选中一个同时满足
0 <= i < nums.length - 1
和nums[i] <= nums[i + 1]
的整数i
。将元素nums[i + 1]
替换为nums[i] + nums[i + 1]
,并从数组中删除元素nums[i]
。
返回你可以从最终数组中获得的 最大 元素的值。
示例 1:
输入:nums = [2,3,7,9,3]
输出:21
解释:我们可以在数组上执行下述操作:
- 选中 i = 0 ,得到数组 nums = [5,7,9,3] 。
- 选中 i = 1 ,得到数组 nums = [5,16,3] 。
- 选中 i = 0 ,得到数组 nums = [21,3] 。
最终数组中的最大元素是 21 。可以证明我们无法获得更大的元素。
示例 2:
输入:nums = [5,3,3]
输出:11
解释:我们可以在数组上执行下述操作:
- 选中 i = 1 ,得到数组 nums = [5,6] 。
- 选中 i = 0 ,得到数组 nums = [11] 。
最终数组中只有一个元素,即 11 。
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^6
思路
本题最重要的一点是理解题意,题意并不是从第一个元素开始计算,而且会计算新的升序序列!
题意是找到数组内部的升序序列,升序序列数值累加,累加完了还需要再回过头去看前面有没有新的升序!
这种情况如果正序遍历,就需要遍历不止一遍,正序要遍历很多遍才能把所有的升序序列都相加!(因为会出现新的升序序列)
因此这种情况需要想到倒序遍历,如果倒着进行遍历,那么nums[i+1]>=nums[i]
的情况,例如[5,3,3]和[2,3,4]这样的情况,就是先进行后面的3+4,再把3+4的结果和2相加,得到9。
否则如果先进行2+3,那么后面的4就会不满足升序条件,导致结果没有+4的部分!
核心点是研究用例,发现这道题并不是从第一个开始遍历,而是找所有相加后可能存在的升序序列,正序需要反复遍历,倒序只需要遍历一遍。
并不需要删除nums[i]!因为倒序遍历,nums[i]就只会遍历一遍,并且nums[i-1]进行累加之后,数值一定大于nums[i]!
最开始的写法:
- 实际上不需要
erase(nums.erase(nums.begin() + i));
,因为i本来后面也不会遍历第二遍了而且[i-1]的数值一定>[i]
#include <vector>
#include <algorithm>
class Solution {
public:
long long maxArrayValue(std::vector<int>& nums) {
//倒序遍历,替换i-1
for (int i = nums.size() - 1; i > 0; --i) {
if (nums[i] <= nums[i-1] ) {
nums[i-1] = nums[i] + nums[i-1];
//nums.erase(nums.begin() + i);
}
}
return *max_element(nums.begin(), nums.end());
}
};
debug测试:执行错误,超出Int范围
Line 6: Char 37: runtime error: signed integer overflow: 2147098116 + 801073 cannot be represented in type ‘int’ (solution.cpp) SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:15:37
原因是Nums是Int类型,直接在原数组上进行取代,容易造成Int越界。
写法1:nums存进long long数组
- 可以直接把nums复制进一个类型为long long的vector数组里
class Solution {
public:
long long maxArrayValue(std::vector<int>& nums) {
std::vector<long long> ans(nums.begin(), nums.end());
for (int i = nums.size() - 1; i > 0; i--) {
if (ans[i-1] <= ans[i] ) {
ans[i-1] = ans[i] + ans[i-1];
//这里的erase写不写都行,是否删除i并没有影响,i后面也不会再遍历了
nums.erase(nums.begin() + i);
}
}
return *max_element(ans.begin(), ans.end());
}
};
写法2:用一个count存储最大值
- 这种写法无需建立 long long数组!是空间上的优化
错误写法:cur需要一直自己累积,到了不是升序的地方才重新计算
class Solution {
public:
long long maxArrayValue(std::vector<int>& nums) {
long long count=nums[nums.size()-1];
long long cur = count;
for (int i = nums.size() - 1; i > 0; i--) {
//如果用count存储,那么需要单独用cur来遍历
//如果是升序那么cur更新
if (nums[i-1] <= cur ) {
cur = nums[i]+nums[i-1];//这里的写法错误,应该是cur+nums[i-1],要一直累积
//同时更新记录值
count = (count>cur) ? count:cur;
}
else{
//如果不是升序,那么cur继续移动,i会自己--,此时cur相当于重新开始计算
cur = nums[i-1];
}
}
return count;
}
};
修改:
class Solution {
public:
long long maxArrayValue(std::vector<int>& nums) {
long long ans = nums[nums.size()-1];//存最大值
long long cur = ans;//遍历,找升序元素的和,不是升序就重新算
for(int i=nums.size()-1;i>0;i--){
//cur在这里表示累积之后的数值,是新的取代原数组的数值
if(cur>=nums[i-1]){
//如果是升序,那么一直累加
cur = cur+nums[i-1];
}
else{
//如果不是升序,那么重新计算
cur = nums[i-1];
}
ans = (ans>cur)?ans:cur;//这一句要放在外面是因为类似[4,3]这样的情况,是不是升序都要更新最大元素
}
return ans;
}
};
- 要掌握第二种空间优化的方法,用cur来进行遍历!