Maps - Забони барномасозии C++ барои барномасозии рақобатӣ
дарс кушода мешавад…
Week 11Sets, Maps, Multisets and Key-Based Data Processing
Maps
A map keeps a value for every key: adding and replacing, counting with cnt[x]++, walking in key order, looking without adding, erasing keys in a sliding window, lookup tables, and pairs as keys.
Назария
Дарси 3 аз 7
Хониш: 30 дақиқа
Саволҳо: 6
A set remembers which values it has seen. A map remembers something for
each key: a phone number for each name, a count for each word, a price for each
item on the menu. It is a table of key → value pairs, each key once, kept
sorted by key.
A value for every key
map<K, V> names two types: the key's and the value's. m[key] = value adds
the key, or replaces its value if the key is already there — a map never holds
the same key twice. m[key] on its own reads the value, and m.size() is the
number of keys.
Counting with a map
The most common map in a solution counts things:
The first time x comes up it isn't a key yet. cnt[x] then adds it with
the value 0 — the starting value for any number — and ++ makes it 1. After
that, every cnt[x]++ finds the key and adds one. Watch the keys arrive:
Each element of a map is a pair: p.first is the key and p.second its value.
A range-for visits them in increasing order of the key — which is all this
problem needs:
The first line holds n, the second n words. Print how many different
words there are, then every different word in alphabetical order with the
number of times it appeared.
For the input 7 and sun rain sun snow rain sun fog it prints
cnt.size() is the number of different words, and the walk comes out
alphabetical with no sorting of your own. On Codeforces and AtCoder (C++17 or
newer) you may also see for (auto [word, c] : cnt); older compilers, like the
one in many Code::Blocks installs, reject that line, while p.first and
p.second work everywhere.
Looking without adding
m[key]adds the key if it is missing — with the value 0 for a number,
or an empty string for a string. That is exactly right for counting, and a
quiet bug when you only wanted to look:
It prints 0, 3, 0, 3, cake costs 12 and 2. Reading price["juice"]
gave 0 — and added "juice" to the map, until erase took it out again at the
end. count and find only look:
m.count(key) is 1 if the key is there, 0 if not;
m.find(key) gives the key's position, or m.end() if it is missing. At
that position it->first is the key and it->second its value.
So: use [] when you are going to write the value anyway (cnt[x]++,
m[key] = v), and count or find when you only want to ask.
Try it on a phone book — and see what each line really does to it:
The phone bookсанҷида бин!
Removing a key
m.erase(key) removes the key and its value; if the key isn't there, nothing
happens.
You need it when counts go down as well as up. A classic case is a
window — the k values in a row starting at some position:
An array of n numbers and a window length k are given. For every window
of k values in a row, print how many different values it holds.
Keep a map of counts for the values inside the window. Moving the window one
step to the right, one value comes in on the right and one leaves on the left —
so two counts change, and nothing else needs recounting. The answer for each
window is cnt.size(), with one catch: a key whose count drops to 0 is still a
key, and cnt.size() still counts it. So when a count reaches zero, erase the
key — then cnt.size() is the number of values really in the window. Here is
every window of 3:
A window slides: one value in, one value out
C++
1#include <bits/stdc++.h>
2using namespace std;
3
4int main() {
5 ios_base::sync_with_stdio(false);
6 cin.tie(NULL);
7
8 vector<int> a = {5, 7, 5, 9, 9, 5};
9 int k = 3;
10 map<int, int> cnt; // value -> copies inside the window
11 for (int i = 0; i < (int)a.size(); i++) {
12 cnt[a[i]]++; // a[i] comes into the window
13 if (i >= k) {
14 cnt[a[i - k]]--; // a[i - k] leaves it
15 if (cnt[a[i - k]] == 0) {
16 cnt.erase(a[i - k]); // no copies left: remove the key
17 }
18 }
19 if (i >= k - 1) {
20 cout << cnt.size() << " ";
21 }
22 }
23 cout << "\n";
24 return 0;
25}
The values
сатр 8
For every 3 values in a row, count how many different values there are.
a6 катак
5
7
5
9
9
5
0
1
2
3
4
5
map<int, int> cntЛуғат
холӣ
k—i—
пахш кун
1 / 40
Lookup tables
A map can also be filled once, by you, and then used to translate names into
numbers you can compute with — days of the week, months, chess pieces. Like
here:
The first line holds q. Each of the next q lines holds two days of the
week, written Mon, Tue, Wed, Thu, Fri, Sat or Sun. For each
line, print how many days forward it is from the first day to the second.
For Mon Thu, Fri Tue and Sun Sun it prints 3, 4 and 0.
day[to] - day[from] can be negative,
and in C++ -3 % 7 is -3, not 4. Adding 7 and taking % 7 again fixes that:
(d % 7 + 7) % 7 is in 0..6 for any whole number d.
Keys of any kind
A key can be any type that < can compare: int, long long (IDs up to
10¹⁸), char, string — and a pair, when one thing is described by two
numbers, like a point on a grid. Pairs compare by first, and by second when
the firsts are equal. A problem with points as keys:
The first line holds n, the next n lines a point x y each; the same
point can come more than once. Print every different point once, sorted by
x and then by y, with the number of times it appeared.
For the points (1, 2), (3, -1), (1, 2), (0, 0), (1, 2) it prints
{x, y} builds the pair right inside the brackets: cnt[{x, y}],
cnt.count({x, y}), cnt.erase({x, y}).
What it costs
A map is a balanced tree of keys, like a set: [], count, find and erase
each take about log₂ n steps, and walking it takes n. With string keys,
each step also compares two strings, so long keys cost a little more.
You will see unordered_map in other people's solutions. It does the same
jobs without keeping the keys in order, and is often faster, but on Codeforces
its tests can be built to make it very slow. map is the safe choice.
Check your understanding
Did you know?
The first dictionary to explain English words in English, Robert Cawdrey's A
Table Alphabeticall, appeared in 1604 with about 2 500 "hard usuall English
wordes". It was a map from words to meanings, sorted by key. Its preface told
readers to learn the order of the letters first, so they could jump to the
right part of the book instead of reading from the start — much as find does.
map<string, int> phone; // key: a name, value: a numberphone["Mia"] = 4021; // adds the key "Mia"phone["Ali"] = 3310;phone["Mia"] = 5577; // "Mia" is already a key: its value is replacedcout << phone["Mia"]; // 5577cout << phone.size(); // 2
cnt[x]++;
худро санҷ1 хол
m is an empty map<string, int>. What do these two lines print?
Як ҷавобро интихоб кунед
Барои ҷавоб додан ва нигоҳ доштани пешрафт ворид шавед.Ворид шудан
худро санҷ1 хол
if (cnt[x] > 0) only looks at the map: it never changes it.
Барои ҷавоб додан ва нигоҳ доштани пешрафт ворид шавед.Ворид шудан
худро санҷ1 хол
What is cnt.size() after these lines?
Барои ҷавоб додан ва нигоҳ доштани пешрафт ворид шавед.Ворид шудан
худро санҷ1 хол
What does this print?
Як ҷавобро интихоб кунед
Барои ҷавоб додан ва нигоҳ доштани пешрафт ворид шавед.Ворид шудан
худро санҷ1 хол
Keys are added to a map<string, int> in the order "pear", "fig", "apple". Which key does a range-for over the map visit first?
Як ҷавобро интихоб кунед
Барои ҷавоб додан ва нигоҳ доштани пешрафт ворид шавед.Ворид шудан
худро санҷ1 хол
A map<pair<int, int>, int> has the keys {2, 1}, {1, 5} and {1, 2}. Which key comes first in a walk?
Як ҷавобро интихоб кунед
Барои ҷавоб додан ва нигоҳ доштани пешрафт ворид шавед.Ворид шудан
4 different words
fog 1
rain 2
snow 1
sun 3
0 0: 1
1 2: 3
3 -1: 1
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int n; cin >> n; map<string, int> cnt; // word -> how many times it appeared for(int i = 0; i < n; i++){ string w; cin >> w; cnt[w]++; // a new word starts at 0, then becomes 1} cout << cnt.size()<< " different words"<< "\n"; for(auto p : cnt){ // keys in alphabetical order cout << p.first<< " "<< p.second<< "\n";} return 0;}
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); map<string, int> price; price["tea"] = 5; price["cake"] = 12; cout << price["juice"]<< "\n"; // careful: [] adds "juice" with the value 0 cout << price.size()<< "\n"; // 3 keys now cout << price.count("milk")<< "\n"; // count() only looks: 1 or 0 cout << price.size()<< "\n"; // still 3 auto it = price.find("cake"); // find() gives the key's place, or end() if it is missing if(it != price.end()){ cout << it->first<< " costs "<< it->second<< "\n";} price.erase("juice"); // removes the key and its value cout << price.size()<< "\n"; return 0;}
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); vector<string> names ={"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; map<string, int> day; // a lookup table: name -> its number for(int i = 0; i < 7; i++){ day[names[i]] = i;} int q; cin >> q; for(int i = 0; i < q; i++){ string from, to; cin >> from >> to; int d = day[to] - day[from]; // can be negative: from Fri to Tue is 1 - 4 = -3 d =(d % 7 + 7) % 7; // turns any whole number into 0..6 cout << d << "\n";} return 0;}
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int n; cin >> n; map<pair<int, int>, int> cnt; // the key is a whole point (x, y) for(int i = 0; i < n; i++){ int x, y; cin >> x >> y; cnt[{x, y}]++; // {x, y} makes the pair} for(auto p : cnt){ // by x, and by y when the x's are equal cout << p.first.first<< " "<< p.first.second<< ": "<< p.second<< "\n";} return 0;}