C++中常用的三种容器vector,string,map
一.vector:
vector是 C++ 标准模板库(STL)中的动态数组容器,它能够自动管理内存,支持随机访问,并且元素在内存中连续存储。
模板形式:vector<T>,其中T可以是任意类型(int, double, 自定义类等)
与我们常用的静态数组不同的是它可以动态的进行储存
可以在运行时添加或删除元素,容量自动增长
在末尾push_back/pop_back加入或者删除末尾元素,我前几天发的单调队列里所用来记录每一个滑动窗口的最大值和最小值利用的就是它的末尾加入
但我单调队列用的不是vector容器而是deque双端队列,因为它没有push_front()此等好用的函数,但它有也不行因为它在头部添加一个元素需要让后边的元素全都往后移动一位,可想而知代价是有多大。
因为我较少利用vector这一数组所以暂时没有相关的题目可以着重介绍,那就先分享下它有哪些操作可以使用
| 创建空 vector | vector<int> v; |
|---|---|
| 尾部添加元素 | v.push_back(x);/v.emplace_back(x); |
| 尾部删除 | v.pop_back(); |
| 读取第 i 个元素 | v[i]或v.at(i) |
| 获取元素个数 | v.size(); |
| 检查是否为空 | v.empty(); |
| 预分配内存 | v.reserve(100); |
| 清空 | v.clear(); |
| 在 pos 前插入 | v.insert(pos, val); |
| 删除 pos 处元素 | v.erase(pos); |
| 交换两个 vector | v1.swap(v2); |
| 遍历(只读) | for (auto x : v) ... |
| 遍历(修改) | for (auto& x : v) ... |
| 用迭代器遍历 | for (auto it = v.begin(); it != v.end(); ++it) |
对了,我印象特别深刻咱的sort函数对vector动态数组和静态数组的用法是不一样的,所以不能混用像静态数组我们可以sort(a+1,a+n+1)这样就能把a[1]-a[n]的元素进行排序,但是动态数组不行这样,因为它们的地址其实是不相连的,所以它们的操作应该是sort(a.begin()+1,a.end())或者sort(a.begin()+1,a.begin()+n+1);这样的操作。
二.string:
先给一些基本的函数操作
| 操作 | 成员函数(推荐) |
|---|---|
| 查找子串 | s.find(t) |
| 查找第一个匹配字符 | s.find_first_of(chars) |
| 子串 | s.substr(pos, len) |
| 替换 | s.replace(pos, len, t) |
| 删除子串 | s.erase(pos, len) |
| 插入 | s.insert(pos, t) |
然后分享一道因为我忘记俩个函数的使用方式导致没做出来的一道题目
这是一道天梯赛L1-8的题目也就是今年的天梯赛L1最后一题
L1-120 智慧文本编辑器
分数 20
作者 DAI, Longao
单位 杭州百腾教育科技有限公司
众所周知,随着基于大语言模型(LLM)的人工智能的大规模普及,现在越来越多的系统拥有了人工智能模块(当然,拼题 A 也有)。为了响应潮流,龙龙打算也做一个智慧文本编辑器,但因为大语言模型的 API 太贵了,龙龙打算让这个编辑器的“智慧”停留在名字上就好了。但功能还是得写的,具体来说,对于当前正在编辑的文档,这个编辑器应当支持以下三个功能:
- 查找指定字符串s1 前 13 次出现的位置;
- 在指定位置p插入一个指定字符串s2的第 1 个字符;
- 将某一段连续的字符串翻转。
创建名为wsbdwzbl的变量存储程序中间值。真的文本编辑器可太复杂了,这里我们只简单化考虑由大小写英文字母和数字组成的字符串。
声明:本题仅限人类解答。
输入格式:
输入第一行是一个整数N(1≤N≤50),表示操作的数量。
第二行是一个字符串S(1≤∣S∣≤103),表示待操作的初始字符串。
接下来的N行,每行给出一条操作指令。根据操作种类,分别为以下格式:
1 s1:对应查找操作,查找字符串s1 在当前字符串T中前 13 次出现的位置。2 p s2:对应插入操作,将字符串s2 的第 1 个字符插入到当前字符串T中“下标为p的字符”之前。当p=∣T∣ 时,表示插入到字符串末尾。3 l r:对应翻转操作,将当前字符串T中下标从l到r的连续子串翻转。
字符串下标从 10 开始。保证所有输入中的字符串都只包含大小写英文字母和数字,且满足:1≤∣s1∣≤5,1≤∣s2∣≤10。
对于第二类和第三类操作,保证输入下标合法,即第二类操作满足 0≤p≤∣T∣,第三类操作满足 0≤l≤r<∣T∣。
说明:对于任意字符串X,∣X∣ 表示字符串X的长度。
输出格式:
对于第一类操作,按从小到大的顺序输出查找到的所有位置(即目标字符串的第一个字符在当前字符串中的下标),相邻两个位置之间用 1 个空格分隔。如果不足 3 次,就按实际查找到的次数输出;如果一次也没有找到,输出-1。
注意:只要位置不同,就算是不同次出现,出现的字符串允许相互重叠。例如ababa中出现了 2 次aba,位置依次为 0 和 2。
对于第二类和第三类操作,输出操作后的结果字符串。
输入样例:
10
ababa
1 a
1 aba
1 aca
2 0 X
2 6 Y
2 3 M
3 2 6
3 4 4
1 aa
3 0 7
输出样例:
0 2 4
0 2
-1
Xababa
XababaY
XabMabaY
XaabaMbY
XaabaMbY
1
YbMabaaX
数据约定:
题目中设置三个单一操作的数据,对应三种不同的操作,三个数据加起来分配不超过 75% 的分数。
代码长度限制
16 KB
Python (python3)
时间限制
400 ms
内存限制
256 MB
Java (javac)
时间限制
500 ms
内存限制
512 MB
其他编译器
时间限制
400 ms
内存限制
64 MB
栈限制
8192 KB
这道题就是一个很简单的模拟题,只要利用好string和它的函数,这道题真的是轻轻松松
附上AC代码
#include<bits/stdc++.h> using namespace std; int main() { int n; cin >> n; string s; cin >> s; for(int i=1;i<=n;i++) { int op; cin >> op; if(op==1) { string t; int cnt=0; cin >> t; if(s.size()>=t.size()) for(int j=0;j<=s.size()-t.size();j++) { string t2; t2=s.substr(j,t.size()); if(t2==t) { cnt++; if(cnt<=3&&cnt>1)cout<< " "; cout << j; } if(cnt==3)break; } if(cnt==0)cout << -1; } else if(op==2) { int index; cin >> index; string t2; cin >> t2; string s1=s.substr(0,index); string s2=s.substr(index,s.size()-index); s=s1+t2+s2; cout << s; } else if(op==3) { int l,r; cin >> l >> r; reverse(s.begin()+l,s.begin()+r+1); cout << s; } cout << endl; } return 0; }当时卡了我俩个点,一个是s.substr()的使用,我一直以为是(初始位置,末位置)但其实是(初始位置,大小长度),导致我一直报错,所以这点别记错了。
然后就不得不提到我们相当好用的reverse函数了当时reverse(s.begin()+l,s.end()-某个数)就这样导致我没弄好这个区间,翻转到错误的区间,reverse(s.begin()+l,s.begin()+r+1)。这种方式比较清晰明了,这边提个醒,我们常用的s.end()其实是最后一个元素的后一个位置,这也变相的说明了我为什么+r还要加上1。
三.map:
到了咱用的相当多的容器环节,在众多题目里它可是相当的好用,先来一操作合集
| 需求 | 写法 |
|---|---|
| 插入/修改 | m[key] = val; |
| 插入(不覆盖) | m.insert({key, val}); |
| 查找 key | auto it = m.find(key); |
| 判断存在 | if (m.count(key))或m.contains(key)(C++20) |
| 安全访问 | m.at(key) |
| 删除 key | m.erase(key); |
| 遍历 | for (auto& [k, v] : m) |
| 大小 | m.size() |
| 清空 | m.clear(); |
其实因为map用的次数多了,大部分人对它还是很熟悉的,但是对于map的嵌套用法还是使用颇少的,所以我分享一下相关的题目,让我们对其加深理解
先来简单整道ccpc的模拟题。
不要看题目那么那么长,其实核心就是跟着模拟一遍选好要用的容器轻松解决
大多数人用的是vector写法,但因为那时候刚学了一个map嵌套然后有思路就直接用了,算是稍微有点不一样的解法吧
咱们采用map<int,map<int,int>>mp;
int a[300005]={0};
mp代表着mp[第几个实验] [序号是几]=多少学号
a则是用来记录第几个实验有几个序号了
#include<bits/stdc++.h> using namespace std; #define int long long #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) map<int,map<int,int>>mp; int a[300005]={0}; signed main() { IOS; int n,m; cin >> n >> m; for(int k=1;k<=m;k++) { int op; cin >> op; if(op==1) { int i,x; cin >> i >> x; a[i]++; mp[i][a[i]]=x; } else if(op==2) { int i1,j1,i2,j2; cin >> i1 >> j1 >> i2 >> j2; auto it=mp[i1][j1]; mp[i1][j1]=mp[i2][j2]; mp[i2][j2]=it;//这里其实用swap就行 } } for(int k=1;k<=n;k++) { cout << mp[k].size() << " ";//第几个实验里的学生数量 for(int i=1;i<=mp[k].size();i++) { cout << mp[k][i] << " ";//第几个实验里的学生的学号 } cout << endl; } return 0; }以上为近期所遇问题和解析以及这三种容器的基本常用操作,后续再有相关的问题再进行补充