[C++ STL] std::map 与字典序最小字符串问题

[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 1N100

1 ≤ ∣ S ∣ ≤ 1000 1\le |S|\le 1000 1S1000

输出

输出字典序最小的字符串的编号,若有多个字符串符合条件,则将编号全部输出并且按照从小到大顺序输出

比如有两个符合条件则输出两行

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 N100long long 的长度多为 128,也可用状态压缩(本文不深究)的方法用 long long 表示这个集合。

解答

流程图如下:

Created with Raphaël 2.2.0 开始 创建map<string, vector<int>> m 输入int N i=0 i<N ? 输入string s 向m[s]添加i+1 读取m的首项为a a中是否还有元素 输出元素 结束 yes no yes no

代码如下(值类型使用 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 的威力!


声明:文章为作者原创,未经作者允许禁止转载!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RainbowC0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值