けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

AtCoder ABC 137 C - Green Bin (4Q, 茶色, 300 点)

すごく面白い問題!

問題概要

 N 個の文字列  S_{1}, S_{2}, \dots, S_{N} が与えられる。

これらの文字列から異なる 2 つの文字列を選ぶ方法であって、それらの文字列が互いにアナグラムとなっているものの個数を求めよ。

制約

  •  2 \le N \le 10^{5}

考えたこと

まずは、問題の条件をわかりやすく言い換えることを考えよう。つまり、「2 つの文字列  S, T が互いにアナグラムである」という条件は複雑なので、これをわかりやすく言い換えたいのだ。

互いにアナグラムかどうかを判定する簡単な方法として、


 S, T をアルファベット順にソートして、一致するかどうかを調べる


というものがある。これに気づきさえすれば、あとは簡単だ。与えられた各文字列  S_{1}, \dots, S_{N} について、それぞれソートしておく。そうした上で、次の連想配列を定義しよう。


nums[str]:文字列  S_{1}, \dots, S_{N} のうち、ソート後の文字列が str であるようなものの個数


これさえ求まれば、あとは、各文字列 str に対して、その個数を  n として

 {}_{n}\mathrm{C}_{2} = \frac{n(n-1)}{2}

を足していけば良い。

計算量は。文字列  S_{i} の長さの最大値を  A として、 O(A N \log N) となる。

コード

#include <bits/stdc++.h>
using namespace std;

int main() {
    int N;
    string S;
    cin >> N;

    map<string, long long> nums;
    for (int i = 0; i < N; i++) {
        cin >> S;
        sort(S.begin(), S.end());
        nums[S]++;
    }

    long long res = 0;
    for (auto [str, num] : nums) {
        res += num * (num - 1) / 2;
    }
    cout << res << endl;
}