D355周赛复盘:模拟题*2

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_backsizeempty等等,这使得std::string在使用方式上与其他STL容器(如std::vectorstd::liststd::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 - 1nums[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来进行遍历!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值