『競プロのための標準 C++』を読みながら取ったメモ

zenn.dev

こちらの書籍を読みながら知らなかった項目を実装しながらメモ.

01 string

  • string型で宣言した変数は初期化しなければカラ.サイズは0.
  • string 変数名(n,char型) でchar型の文字をn回繰り返した文字列で初期化できる.
  • stoiでstring型からint型に変換できるが,文字列が"1101"などの二進数文字列の場合,stoi(string, nullptr, 基数) でパースして整数変換できる.
  • .push_back(char型) で末尾に1文字追加,.pop_back()で末尾の1文字削除
  • 文字列の基本の順番は 0 < 9 < A < Z < a < z (参考:ASCIIコード表)で,辞書形式の順番になる.
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);

    string s;
    cout << s << endl;
    cout << s.size() << endl;
    //output
    //
    // 0

    string s5(5, 'a');
    cout << s5 << endl;
    cout << s5.size() << endl;
    //output
    // aaaaa
    // 5

    s = "1101";
    int ss = stoi(s, nullptr, 2);
    cout << ss << endl;
    //output
    // 13

    s = "test";
    s.push_back('a');
    cout << s << endl;
    s.pop_back();
    cout << s << endl;
    //output
    // testa
    // test

    vector<string> sv;
    sv.push_back("Ca");
    sv.push_back("ab");
    sv.push_back("Ab");
    sv.push_back("cA");
    sort(sv.begin(), sv.end());
    for (size_t i = 0; i < sv.size(); i++)
    {
        cout << sv[i] << endl;
    }
    //output
    // Ab
    // Ca
    // ab
    // cA


    return 0;
}

02 vector

  • 初期化
    • vector<Type> 変数名(n, Type(value)) でn個のType(value)の値で初期化
    • valueを指定しない場合(Type())は各型0や0.0,空文字列,falseなどで初期化
    • nも指定しないで vector<Type> 変数名 で定義した場合は要素数0の空配列
  • .empty()で空チェック
  • string型同様,.push_back(), .pop_back()で末尾の追加・削除
  • vector<bool> のみ,.flip() で true/false の反転ができる
  • vector関連ではないが,cout << boolalpha でbool型の出力を0/1ではなくtrue/falseで表示できる)
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);


    vector<int> nums(5, 100);
    cout << nums.size() << endl;
    for (size_t i = 0; i < nums.size(); i++)
    {
        cout << nums[i] << endl;
    }
    //output
    // 5
    // 100
    // 100
    // 100
    // 100
    // 100

    vector<int> nums_test1(3);
    cout << nums_test1.size() << endl;
    for (size_t i = 0; i < nums_test1.size(); i++)
    {
        cout << nums_test1[i] << endl;
    }
    vector<string> nums_test2(2);
    cout << nums_test2.size() << endl;
    for (size_t i = 0; i < nums_test2.size(); i++)
    {
        cout << nums_test2[i] << endl;
    }
    //output
    // 3
    // 0
    // 0
    // 0
    // 2
    //
    //


    vector<int> empty1(3, 1);
    cout << empty1.empty() << endl;
    vector<int> empty2;
    cout << empty2.empty() << endl;
    //output
    // 0(false)
    // 1(true)

    vector<int> pushpopback {5, 10, 3};
    pushpopback.push_back(100);
    for (size_t i = 0; i < pushpopback.size(); i++)
    {
        cout << pushpopback[i] << endl;
    }
    //output
    // 5
    // 10
    // 3
    // 100
    pushpopback.pop_back();
    for (size_t i = 0; i < pushpopback.size(); i++)
    {
        cout << pushpopback[i] << endl;
    }
    //output
    // 5
    // 10
    // 3

    vector<bool> booleans_test = {false, true, false};
    booleans_test.flip();
    for (size_t i = 0; i < booleans_test.size(); i++)
    {
        cout << boolalpha << booleans_test[i] << endl;
    }
    //output
    // true
    // false
    // true

    return 0;
}

03 numeric

  • accumurate(イテレータ最初, イテレータ最後, 初期値, binary_op) で初期値に対して配列の累積和を計算
  • 上記で binary_op を指定した場合はその計算を行いながら処理する(例えば string(), multiplies() など)
  • iota(イテレータ最初, イテレータ最後, 初期値) で初期値から1ずつ増やした値を各要素にセットする.
  • 上記で初期値を 'a' にセットすれば a から順にアルファベットをセットした配列を作れる(ただし,vectorのTypeはcharで定義すること)
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);

    vector<int> vec = {1, 2, 3, 4, 5};
    int s = accumulate(vec.begin(), vec.end(), 0);
    cout << s << endl;
    s = accumulate(vec.begin(), vec.end(), 100);
    cout << s << endl;
    //output
    // 15
    // 115

    vector<string> vec2 = {"apple", "orange", "peach"};
    string s2 = accumulate(vec2.begin(), vec2.end(), string());
    cout << s2 << endl;
    s2 = accumulate(vec2.begin(), vec2.end(), string("header_"));
    cout << s2 << endl;
    //output
    // appleorangepeach
    // header_appleorangepeach

    s = accumulate(vec.begin(), vec.end(), 1, multiplies<>());
    cout << s << endl;
    //output
    // 120


    iota(vec.begin(), vec.end(), 11);
    for (size_t i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << endl;
    }
    //output
    // 11
    // 12
    // 13
    // 14
    // 15


    vector<char> vec_char(26);
    iota(vec_char.begin(), vec_char.end(), 'a');
    for (size_t i = 0; i < vec_char.size(); i++)
    {
        cout << vec_char[i] << endl;
    }
    //output
    // a
    // b
    // .................
    // z


    return 0;
}

04 unordered_set

そもそも unordered_set を知らなかったのでググった.

主な set との違いを unordered_set - cpprefjp C++日本語リファレンス より引用.

unordered_set は、同一キーの要素を複数格納できず、格納順が規定されていないコンテナである。

コンテナ内の各要素は、キーのハッシュ値に基づきハッシュテーブルに格納されるため、決められた順序で並んでいるわけではない。

  • set は二分探索木で実装されているが,unordered_set はハッシュ値に基づくため,要素の順が規定されていない
  • vector からもイテレータを指定することで unordered_set に変換可能
  • 格納順が異なっても格納内容が一致すれば各コンテナの比較 == で true となる(逆に格納内容が一致しなければ false)
  • .count(Key) で指定した要素の個数(重複を許さないので 0 or 1)を取得可能
  • .insert(Value) で要素追加できる
    • 既に同じ要素が存在する場合は insert(Value).second で戻り値(bool)が false となる
  • erase(Key),.clear() なども vector 同様に使える
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);

    unordered_set<string> uos_colors = {"orange", "pink", "red", "green", "blue", "red"};
    cout << uos_colors.size() << endl;
    for (auto itr = uos_colors.begin(); itr != uos_colors.end(); itr++)
    {
        cout << *itr << endl;
    }
    //output
    // 5
    // blue
    // green
    // red
    // pink
    // orange

    set<string> s_colors = {"orange", "pink", "red", "green", "blue", "red"};
    cout << s_colors.size() << endl;
    for (auto itr = s_colors.begin(); itr != s_colors.end(); itr++)
    {
        cout << *itr << endl;
    }
    //output
    // 5
    // blue
    // green
    // orange
    // pink
    // red

    vector<string> v_colors = {"orange", "pink", "red", "green", "blue", "red"};
    unordered_set<string> from_vector (v_colors.begin(), v_colors.end());
    cout << from_vector.size() << endl;
    for (auto itr = from_vector.begin(); itr != from_vector.end(); itr++)
    {
        cout << *itr << endl;
    }
    //output
    // 5
    // blue
    // green
    // red
    // pink
    // orange


    unordered_set<string> uos_colors2 = {"red", "orange", "blue", "blue", "green", "pink"};
    cout << "--" << endl;
    for (auto itr = uos_colors2.begin(); itr != uos_colors2.end(); itr++)
    {
        cout << *itr << endl;
    }
    //output
    // pink
    // green
    // blue
    // orange
    // red
    unordered_set<string> uos_colors3 = {"red", "orange", "green", "pink"};
    cout << "--" << endl;
    for (auto itr = uos_colors3.begin(); itr != uos_colors3.end(); itr++)
    {
        cout << *itr << endl;
    }
    //output
    // green
    // orange
    // pink
    // red

    cout << boolalpha << (uos_colors == uos_colors2) << endl;
    cout << boolalpha << (uos_colors == uos_colors3) << endl;
    //output
    // true
    // false

    cout << uos_colors.count("red") << endl;
    cout << uos_colors.count("white") << endl;
    //output
    // 1
    // 0

    uos_colors.insert("gray");
    cout << "--" << endl;
    for (auto itr = uos_colors.begin(); itr != uos_colors.end(); itr++)
    {
        cout << *itr << endl;
    }
    //output
    // gray
    // blue
    // green
    // red
    // pink
    // orange

    uos_colors.erase("gray");
    cout << "--" << endl;
    for (auto itr = uos_colors.begin(); itr != uos_colors.end(); itr++)
    {
        cout << *itr << endl;
    }
    //output
    // blue
    // green
    // red
    // pink
    // orange

    uos_colors.clear();
    cout << uos_colors.size() << endl;
    //output
    // 0

    return 0;
}

05 algorithm

ほとんど知らなかった項目なので,今後自分が使いそうな項目のみメモ.

  • 3つ以上の数値の最大値・最小値はリスト{...} を使って min({...}) のように書ける
  • min_element(itrFirst, itrLast) で配列の最小値のイテレータを取得でき,*をつけるとその値を取得できる
  • distance(itrFirst, min_element()) で最小値の位置を取得できる
  • minmax(変数 or リスト) で与えられた値の最小値,最大値の pair を取得でき,最小値は pair.first, 最大値は pair.second で取得できる
  • fill(itrFirst, itrLast, value) で配列を value で埋める
  • remove(itrFirst, itrLast, value) で配列中から value を削除して,削除した要素を埋めて並び直し(削除して余った領域は無効領域として残る),有効範囲のイテレータを返す
  • 上記の無効領域を削除するのが .erase で,.erase(remove(), itr.Last) で削除
    • remove と erase の説明はもとの書籍の図がわかりやすい こちら
    • 文字列に関しても上記で同様に削除できる(文字列が格納されている配列に対して value を文字列で指定)
  • remove_if(itrFirst, itrLast, unaryPred) で上記の条件付き削除が可能
    • unaryPred : []<Type>(return 条件) (例:{ return (n % 2 == 0); })
  • reverse(itrFirst, itrLast) で要素を逆順にする
  • rotate(itrFirst, itrMId, itrLast) で,itrMid で指定した要素が先頭で itrLast まで続き,そのあとから残りの要素が itrFirst 続くように並び替える
  • unique(itrFirst, itrLast) で隣り合った要素が同じにならないように要素を削除して,削除した要素を埋めて並び直し(削除して余った領域は remove 同様無効領域とする),有効範囲のイテレータを返す
  • remove と同様,unique で返ってきたイテレータerase(unique(), itr.Last) とすることで,無効領域を削除できる
    • sort 後,unique → erase とすれば重複要素の削除ができる(setでもできるが)
  • sort(itrFirst, itrLast, greater<>{}) で要素が大きい順に並べる(指定しない場合は小さい順)
  • nth_element(itrFirst, itrNth, itrLast) で,itrNth 番目の要素の値が,配列全体のうち N 番目に小さい値となるようにソートする(ただし,itrNth の前後は順不定
  • lower_bound(itrFirst, itrLast, value) でソート済みの配列に対して value を挿入しようとする際,ソート状態を維持できかつ最も左の位置を取得し,その位置を示すイテレータを返す
  • 実際に何番目となるかは distance(itrFirst, lowe_bound()) で取得
  • lower_bound は最も左となる位置だが,upper_bound は最も右側となる位置
  • equal_range(itrFirst, itrLast, value) で lower_bound と upper_bound 両方の結果を pair で取得できる
  • ソート済みの2つの配列について,
    • 和集合(少なくともどちらか1つの配列に存在する要素集合)は set_union(itrFirst1, itrLast1, itrFirst2, itrLast2, itrOut) で取得可能
    • 積集合(両方の配列に存在する要素集合)は set_intersection(itrFirst1, itrLast1, itrFirst2, itrLast2, itrOut) で取得可能
    • 差集合(前者の集合から後者に存在する要素を取り除いた集合) は set_difference(itrFirst1, itrLast1, itrFirst2, itrLast2, itrOut) で取得可能
    • 対称差集合(2つの配列のどちらかにしか存在しない要素集合)は set_symmetric_difference(itrFirst1, itrLast1, itrFirst2, itrLast2, itrOut) で取得可能
    • 和集合のように重複した要素を削除することなく,マージした集合を得るのは merge(itrFirst1, itrLast1, itrFirst2, itrLast2, itrOut) で取得可能
      • いずれの場合も計算結果を itrOut で示したイテレータに格納できる(例:空の配列に back_inserter(配列名) で格納できる)
  • next_permutation(itrFirst, itrLast) で辞書順に次の順列を取得できる
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);

    cout << "--" << endl;
    int a = 30, b = 40, c = 10;
    cout << min({a, b, c}) << endl;
    //output
    // 10

    cout << "--" << endl;
    vector<int> data = {100, -3, 0, 10, 40, 1};
    cout << *min_element(data.begin(), data.end()) << endl;
    cout << distance(data.begin(), min_element(data.begin(), data.end())) << endl;
    //output
    // -3
    // 1

    cout << "--" << endl;
    auto p = minmax({a, b, c});
    cout << p.first << " " << p.second << endl;
    //output
    // 10 40

    cout << "--" << endl;
    fill(data.begin(), data.end(), 0);
    for (size_t i = 0; i < data.size(); i++)
    {
        cout << data[i] << endl;
    }
    //output
    // 0
    // 0
    // 0
    // 0
    // 0
    // 0

    cout << "--" << endl;
    data = {100, -3, 0, 10, 40, 1};
    auto itrRemove = remove(data.begin(), data.end(), 0);
    data.erase(itrRemove, data.end());
    for (size_t i = 0; i < data.size(); i++)
    {
        cout << data[i] << endl;
    }
    //output
    // 100
    // -3
    // 10
    // 40
    // 1

    cout << "--" << endl;
    auto itrRemove2 = remove_if(data.begin(), data.end(), [](int n){return (n%2==0);});
    data.erase(itrRemove2, data.end());
    for (size_t i = 0; i < data.size(); i++)
    {
        cout << data[i] << endl;
    }
    //output
    // -3
    // 1


    cout << "--" << endl;
    data = {100, -3, 0, 10, 40, 1};
    reverse(data.begin(), data.end());
    for (size_t i = 0; i < data.size(); i++)
    {
        cout << data[i] << endl;
    }
    //output
    // 1
    // 40
    // 10
    // 0
    // -3
    // 100


    cout << "--" << endl;
    data = {100, -3, 0, 10, 40, 1};
    rotate(data.begin(), data.begin() + 2, data.end());
    for (size_t i = 0; i < data.size(); i++)
    {
        cout << data[i] << endl;
    }
    //output
    // 0
    // 10
    // 40
    // 1
    // 100
    // -3

    cout << "--" << endl;
    data = {1, 1, 0, 10, 10, 3, 4, 5, 5};
    auto itrUnique = unique(data.begin(), data.end());
    data.erase(itrUnique, data.end());
    for (size_t i = 0; i < data.size(); i++)
    {
        cout << data[i] << endl;
    }
    //output
    // 1
    // 0
    // 10
    // 3
    // 4
    // 5

    cout << "--" << endl;
    sort(data.begin(), data.end(), greater<>{});
    for (size_t i = 0; i < data.size(); i++)
    {
        cout << data[i] << endl;
    }
    //output
    // 10
    // 5
    // 4
    // 3
    // 1
    // 0


    cout << "--" << endl;
    data = {100, -3, 0, 10, 40, 1};
    nth_element(data.begin(), data.begin()+2, data.end());
    cout << data[2] << endl;
    //output
    // 1


    cout << "--" << endl;
    data = {100, -3, 0, 10, 40, 1};
    sort(data.begin(), data.end());
    for (size_t i = 0; i < data.size(); i++)
    {
        cout << data[i] << endl;
    }
    //output
    // -3
    // 0
    // 1
    // 10
    // 40
    // 100
    cout << "--" << endl;
    auto itr = lower_bound(data.begin(), data.end(), -1);
    cout << *itr << endl;
    cout << distance(data.begin(), itr) << endl;
    //output
    // 0
    // 1

    cout << "--" << endl;
    itr = lower_bound(data.begin(), data.end(), 0);
    cout << *itr << endl;
    cout << distance(data.begin(), itr) << endl;
    //output
    // 0
    // 1

    cout << "--" << endl;
    itr = upper_bound(data.begin(), data.end(), 0);
    cout << *itr << endl;
    cout << distance(data.begin(), itr) << endl;
    //output
    // 1
    // 2

    cout << "--" << endl;
    auto itr_equal = equal_range(data.begin(), data.end(), 0);
    cout << *itr_equal.first << endl;
    cout << distance(data.begin(), itr_equal.first) << endl;
    cout << *itr_equal.second << endl;
    cout << distance(data.begin(), itr_equal.second) << endl;
    //output
    // 0
    // 1
    // 1
    // 2

    cout << "--" << endl;
    vector<int> vec1 = {0, 2, 5, 9};
    vector<int> vec2 = {2, 3, 4, 5};
    vector<int> vec3;
    set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), back_inserter(vec3));
    for (size_t i = 0; i < vec3.size(); i++)
    {
        cout << vec3[i] << endl;
    }
    //output
    // 0
    // 2
    // 3
    // 4
    // 5
    // 9

    vec3.clear();

    cout << "--" << endl;
    set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), back_inserter(vec3));
    for (size_t i = 0; i < vec3.size(); i++)
    {
        cout << vec3[i] << endl;
    }
    //output
    // 2
    // 5


    vec3.clear();

    cout << "--" << endl;
    set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), back_inserter(vec3));
    for (size_t i = 0; i < vec3.size(); i++)
    {
        cout << vec3[i] << endl;
    }
    //output
    // 0
    // 9

    vec3.clear();

    cout << "--" << endl;
    set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), back_inserter(vec3));
    for (size_t i = 0; i < vec3.size(); i++)
    {
        cout << vec3[i] << endl;
    }
    //output
    // 0
    // 3
    // 4
    // 9


    vec3.clear();

    cout << "--" << endl;
    merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), back_inserter(vec3));
    for (size_t i = 0; i < vec3.size(); i++)
    {
        cout << vec3[i] << endl;
    }
    //output
    // 0
    // 2
    // 2
    // 3
    // 4
    // 5
    // 5
    // 9


    cout << "--" << endl;
    data = {1, 2, 3, 4};
    do
    {
        for (size_t i = 0; i < data.size(); i++)
        {
            cout << data[i] << " ";
        }
        cout << endl;
    } while (next_permutation(data.begin(), data.end()));
    //output
    // 1 2 3 4
    // 1 2 4 3
    // 1 3 2 4
    // 1 3 4 2
    // 1 4 2 3
    // 1 4 3 2
    // 2 1 3 4
    // 2 1 4 3
    // 2 3 1 4
    // 2 3 4 1
    // 2 4 1 3
    // 2 4 3 1
    // 3 1 2 4
    // 3 1 4 2
    // 3 2 1 4
    // 3 2 4 1
    // 3 4 1 2
    // 3 4 2 1
    // 4 1 2 3
    // 4 1 3 2
    // 4 2 1 3
    // 4 2 3 1
    // 4 3 1 2
    // 4 3 2 1


    return 0;
}

06 tuple

省略

07 ios, iomanip

省略