ここで、何が変数で、何が定数であるかを理解することが重要です。
桁数は定数 (10) です。すべての桁数 (1024) も同様です。そのようなセットのすべてのペアの数も同様です (2 20または約 100 万)。それを活かしましょう。
入力を前処理して、サイズが一定の (入力サイズに依存しない) データ構造の「同等の」表現に変換してみましょう。次に、その一定サイズの構造で行うことは、定義上、一定時間の操作であるため、合計実行時間は、前処理フェーズのみによって漸近的に決定されます。
データ構造
1024 個の整数の配列を作成します。各バケット (インデックス) は一連の数字に対応します。各バケットに正確にその桁のセットを持つ入力番号の数を格納したいと考えています。
たとえば、3606 には数字 0、3、および 6 があるため、バケット 2 0 + 2 3 + 2 6 = 73 でカウントされます。
アルゴリズム
前処理は明らかです。次の数字 ( など) を取り、'3'
その値 ( など) に変換し3
、対応するビット ( など1 << 3
) を計算し、それを (暫定的な) バケット インデックス変数に OR します。異なる数字は異なるビットに存在するため、すべての数字の組み合わせが一意のバケットを取得しますが、数字の繰り返しは削除されました。数値区切り記号に遭遇するまで、このようにループします。その時点で、バケット インデックスは最終的なものであり、バケットをインクリメントし、バケット インデックスをリセットして、次の桁までスキップすることができます。
それでおしまい。あとは羊を数えるだけです。おっとっと。羊のペア。
各バケットを他の各バケットと比較します (ただし、それ自体は比較しません)。2 つのインデックスが数字を共有している場合 (これは演算子を使用して決定できます&
)、これら 2 つのバケットの内容を乗算し、その積をグローバルに維持された合計に加算します。
各バケットをそれ自体とも比較し、バケットの内容でx * (x - 1) / 2
あるグローバルに維持された合計にのみ追加x
します。
その合計があなたの結果です。
パフォーマンス
最悪の場合:O(n)
はn
入力サイズです。
定数要因も有利です。桁または区切りごとにいくつかの命令 (および RAM アクセス) が必要でした。一定のフェーズでは、100 万個のバケット ペアを調べ、各ペアに別の一握りの命令のようなものを費やします (RAM アクセスは不要で、データ構造は非常にコンパクトです)。これは超高速です。
理論家は、これは不正行為だと言うでしょう。入力の長さに上限はないと仮定しています (または、漸近的な複雑さについてまったく話すことができませんでした)。さらに、入力の長さの合計まで整数変数に入れることができると仮定しています。しかたがない。
より実践的なプログラマーは、アルゴリズムがアルファベット サイズで指数関数的であることに気付くでしょう。私たちは幸運でした。私たちの単語が数字ではなく、区切り記号を除いて任意の文字で構成されていた場合、私たちのアルゴリズムは依然として漸近的に線形時間アルゴリズムですが、メガバイトまで簡単に処理できる単純なアルゴリズムと比較して、どのような入力に対しても使用できないほど遅くなります。一度に入力します。