目录
[C++ STL] std::map 与字典序最小字符串问题
本章通俗讲解 std::map
,并通过 NOJ 例题展示其应用。如有不当之处还望网友们积极指出。
std::map 容器
std::map
容器不同于一般的序列容器,它存储一对数据:键(key)与值(value)。std::map
存在于头文件 <map> 中。创建 std::map
需指定键与值的类型,添加元素时需同时添加键与值,读写时通过键访问值并对值进行处理,删除元素时以键查找并删除。
当然,C++ STL 还提供一种 std::unordered_map
,二者的用法基本一致,但 std::map
会根据键自动排序,而 std::unordered_map
则通过 hash_table
连接各元素,不会对元素自动排序,而且空间开销更大,但它的优点在于数据的存储和查找更快。std::map
默认按键从小到大排序,当然也可以自定义排序规则(本文不深究)。
创建与初始化
创建时需指定键类型(常为 std::string
)与值类型。创建时可以不初始化,也可以初始化:
#include<iostream>
#include<string>
#include<map> //引入头文件 <map>
int main(void){
//未初始化,默认为空表
std::map<std::string, int> m;
//创建并初始化三组数据
std::map<std::string, int> m_inited{{"CPU", 10}, {"GPU", 15}, {"RAM", 20}};
return 0;
}
std::map
的值类型可以是非容器类型,也可以是容器类型。一般用容器类型记录该键下的一组值。比如:
std::map<std::string, std::vector<int>> m_vector; //数组表
增删改
std::map
实际上存储 std::pair
类型的数据。通常插入元素时,需创建一个 std::pair
对象以实现:
//使用 insert 方法插入元素
m.insert(std::pair<std::string, int>("SSD", 30));
当然还有更简单的写法:
// 使用 emplace 方法一步插入
m.emplace("SSD", 30);
// 或者直接访问键以插入(或更改)。当该键不在 m 内时,将自动添加该键与默认初始值。
m["SSD"] = 30;
删除元素常使用 erase
方法,指定键即可。还有扩展的删除方法 std::erase_if
(C++20起)。
// 使用 erase 方法删除
m.erase("SSD");
// 或者使用 erase_if 方法筛除
std::erase_if(m, [](const auto& pair){ return pair.second > 25; });
访问元素时通过 []
操作符或 at
方法按键访问。也以此方法进行修改。
std::cout << m["SSD"] << std::endl; // 30
m["SSD"]++;
std::cout << m.at("SSD") <<std::endl; //31
遍历
std::map
的遍历方式与其他 STL 容器类似。较简单的遍历方法如下。其中 item 为 std::pair
类型,item->first 为键,item->second 为值。由于 std::map
默认会根据键排序,所以你会看到 item->first 是有序的。若使用 std::unordered_map
则会按照插入元素的先后顺序输出。
for (const auto& item : m) {
std::cout << item->first << ": " << item->second << std::endl;
}
字典序最小字符串问题
描述
问 N N N个字符串中字典序最小的字符串的编号是多少
字符串编号为 1 1 1到 N N N
输入
第一行输入一个正整数 N N N
接下来的N行每行一个字符串 S S S
1 ≤ N ≤ 100 1\le N\le 100 1≤N≤100
1 ≤ ∣ S ∣ ≤ 1000 1\le |S|\le 1000 1≤∣S∣≤1000
输出
输出字典序最小的字符串的编号,若有多个字符串符合条件,则将编号全部输出并且按照从小到大顺序输出
比如有两个符合条件则输出两行
Sample Input 1
4
aa
ab
aa
cb
Sample Output 1
1
3
分析
好吧我承认这个题难度系数不大,不用 C++ STL 也可以很好实现。本讲解主要是为了展示 std::map
的应用。
之前已经说到,std::map
不同于 std::unordered_map
的地方在于,std::map
默认按键对元素进行排序。如果键是字符串类型,则按字典序从小到大排布。因此只需要访问第一个元素便可以确定字典序最小的字符串。
那么值类型应该是什么呢?由于每个字符串可能重复出现,对应不同的位置,我们需要把每个位置都放到这个值里面,因此需要一个集合(比如有序集合std::set
)。由于我们输入时要记录位置,而在循环中,如果这个位置变量递增的话,那么用普通无序序列容器也行(比如 std::vector
)。当然,由于
N
≤
100
N\le 100
N≤100,long long
的长度多为 128,也可用状态压缩(本文不深究)的方法用 long long
表示这个集合。
解答
流程图如下:
代码如下(值类型使用 std::vector<int>
):
#include<iostream>
#include<string>
#include<map>
#include<vector>
using namespace std;
int main(void){
int n;
string s;
cin>>n;
map<string, vector<int>> m;
for(int i=0;i<n;i++){
cin>>s;
m[s].push_back(i+1);
}
auto a=m.begin(); //访问第一个元素的迭代器
for(int i:a->second){
cout<<i<<endl;
}
return 0;
}
这就是 std::map
的威力!
声明:文章为作者原创,未经作者允许禁止转载!