【Python】4つのリストからtarget以下の合計となるユニークなインデックス組み合わせの数を効率的に求める方法
4つの整数リスト A、B、C、D とターゲット値(target)が与えられたとき、A[i] + B[j] + C[k] + D[l] ≤ target を満たすようなインデックスの組み合わせ (i, j, k, l) の総数を求める問題です。
たとえば、入力が A = [3, 2]、B = [5, 3]、C = [1]、D = [2, 3]、target = 9 の場合、出力は 3 になります。条件を満たす組み合わせとしては、[3, 3, 1, 2]、[3, 3, 1, 2]、[2, 3, 1, 3] の3通りが挙げられます。
解法のアプローチ
4つのリストすべての組み合わせを素朴に全列挙すると計算量が O(n⁴) となり、リストが大きくなると現実的な時間で処理できません。そこで、「半分ずつ処理して二分探索で結合する」という手法を使って効率化します。手順は以下のとおりです。
- まず空の一時リスト(temp_list)を用意します。
- リストAとBのすべてのペアについて、その和 A[i] + B[j] をtemp_listの末尾に追加していきます。
- temp_listを昇順にソートします。
- 続いて、リストCとDのすべてのペアについて以下を繰り返します。
- sum_cd := C[i] + D[j](CとDのペアの和)
- sum_ab := target − sum_cd(AとB側に許される残りの上限値)
- ソート済みtemp_listの中から sum_ab 以下の要素の個数を数え、答え(ans)に加算します。
- 最終的なansを返します。
ここでポイントとなるのが、Python標準ライブラリ bisect モジュールの bisect_right() 関数です。ソート済みリストに対して二分探索を行い、指定した値以下の要素数を O(log n) で取得できるため、内側のカウント処理を高速化できます。
実装例
from bisect import bisect_right class Solution: def solve(self, A, B, C, D, target): temp_list = [] for i in range(len(A)): for j in range(len(B)): temp_list.append(A[i] + B[j]) temp_list.sort() ans = 0 for i in range(len(C)): for j in range(len(D)): sum_cd = C[i] + D[j] sum_ab = target - sum_cd ans += bisect_right(temp_list, sum_ab) return ans ob = Solution() A = [3, 2] B = [5, 3] C = [1] D = [2, 3] target = 9 print(ob.solve(A, B, C, D, target))
入力
[3, 2], [5, 3], [1], [2, 3], 9
出力
3
計算量の目安
AとBのペアの生成に O(|A|×|B|)、ソートに O(|A|×|B|・log(|A|×|B|))、さらにCとDの各ペアに対する二分探索に O(log(|A|×|B|)) かかります。全体では O((|A|×|B| + |C|×|D|)・log(|A|×|B|)) 程度に収まり、4重ループによる全列挙 O(n⁴) と比べて大幅に高速です。この「2つのグループに分けて片方をソートし、もう片方から二分探索で対応を数える」テクニックは、4Sum系の問題や「4つの配列から条件を満たす組を見つける」類題でも広く応用できるので、ぜひ覚えておきましょう。
-
Pythonで解く:「a」と「b」の文字列から作成できるユニークな文字列の数を求めるアルゴリズム
「a」と「b」のみで構成された文字列 s があるとします。このとき、「a」はそのまま「a」のままでもよいし、「b」に変換してもかまいません。一方、「b」は一切変更できません。この条件のもとで、作成できるユニークな文字列の総数を求めるのが本問題の目的です。問題の例たとえば、入力が s = baab の場合、出力は 4 になります。これは、以下の4種類の文字列を作成できるためです。baab(元のまま)babbbbabbbbb解法のアプローチこの問題は非常にシンプルな数学的性質を利用して解けます。「a」はそれぞれ独立に「a」または「b」の2択を選べるため、文字列中の「a」の個数を n とすると、組み
-
Pythonプログラムで数の偶数の約数の合計を求める方法
この記事では、以下の問題文に対する解決策について詳しく解説します。 問題文:ある数が与えられたとき、その数のすべての偶数の約数(因子)の合計を求めて表示します。 アプローチ まず、与えられた数が奇数であるかどうかを確認します。奇数には偶数の約数が存在しないため、その場合は 0 を返します。 数が偶数である場合は、実際の計算に進みます。ここでのポイントは、20(つまり1)以外のすべての項を掛け合わせることで、偶数の約数の合計が得られるという点です。 偶数の約数からすべての奇数を取り除くために、20 に相当する「1」を無視します。この処理を行うことで、残るのは偶数の約数のみとなります。なお、2 は