-
Pythonで等間隔に置かれた石をすべて回収する際の総移動距離を求める方法
問題の概要あるレース大会が開催されるとしましょう。道路上には複数の石が一直線に並んで置かれており、スタート地点にはバケツが用意されています。バケツから最初の石までは6単位の距離があり、それ以降の石は互いに4単位ずつ離れて配置されています。参加者はバケツから出発し、最も近い石を拾ってバケツまで戻り、そこに石を入れます。その後、再び走って次の石を取りに行き、また戻ってバケツに入れる——この手順を、すべての石がバケツに収まるまで繰り返します。石がn個あるとき、参加者が移動する必要のある総距離を求めるのがこの問題です。たとえば入力が n = 5 の場合、出力は 140 になります。これは次の計算による
-
Pythonで「左側はすべて小さく、右側はすべて大きい」条件を満たす要素を見つける方法
配列が与えられたとき、「その要素より前にあるすべての要素が小さく、後ろにあるすべての要素が大きい」という条件を満たす要素を見つける問題を考えてみましょう。該当する要素が存在すればそのインデックスを返し、存在しない場合は -1 を返します。 例えば、入力が A = [6, 2, 5, 4, 7, 9, 11, 8, 10] の場合、出力は 4 になります。インデックス 4 の要素「7」の左側には 7 未満の値(6, 2, 5, 4)のみが並び、右側には 7 より大きい値(9, 11, 8, 10)のみが並んでいるためです。 解法のアプローチ この問題を効率的に解くには、以下の手順に従います。
-
【Python】文字ストリームから最初に一度だけ現れる文字を検索する方法
文字ストリーム(あるいは単純な文字列)が与えられ、その中から最初に一度だけ出現する文字を見つける問題を考えてみましょう。例えば、文字列が「people」の場合、出現回数が1回となる最初の文字は「o」であり、そのインデックス 2 を返します。該当する文字が存在しない場合は -1 を返します。 解法のアプローチ この問題は、各文字の出現回数を記録する「頻度マップ(ハッシュマップ)」を使うことで効率的に解けます。手順は以下のとおりです。 空の頻度マップ(辞書)を作成する 文字列内の各文字 c について次の処理を行う c がマップに存在しない場合は、キー c を値 1 で登録する すでに存在する場
-
Pythonで配列の要素が最後にゼロになるインデックスを求める方法
問題概要n個の数値を持つ配列Aと、別の入力Kが与えられたとします。このとき、指定された操作を繰り返し実行した結果、最後にゼロへ減少する要素のインデックスを見つける必要があります。操作のルール操作は次のように定義されています。A[0]からA[N-1]まで順番に、各要素を A[i] = A[i] - K として更新する。A[i] < K となった場合は A[i] = 0 とする。一度0になった要素には、それ以降の操作を行わない。この操作をすべての要素が0になるまで繰り返し、最後にゼロとなる要素のインデックスを返します。具体例で確認例えば、入力が A = [4, 3, 6, 8, 3, 10]
-
Pythonで二分木から最大の完全二分木(パーフェクトサブツリー)を見つける方法
与えられた二分木の中から、最大の完全二分木(Perfect Binary Tree)となっているサブツリーを見つける問題を考えてみましょう。完全二分木とは、すべての内部ノードが必ず2つの子を持ち、すべての葉ノードが同じ深さに位置する二分木のことです。例えば、次のような二分木が入力として与えられた場合を想定します。この場合の出力は 3 となり、見つかったサブツリーは次の通りです。解法のアプローチこの問題は、木を再帰的にたどりながら、各部分木について「完全二分木であるかどうか」と「高さ」を記録していくことで効率的に解けます。具体的な手順は以下の通りです。isPerfect(完全二分木かどうか)、h
-
【Python】列の入れ替えが許されたバイナリ行列で、1だけで構成される最大長方形を求める方法
問題の概要 0と1だけで構成されるバイナリ行列が与えられます。この中から「すべて1で埋まった最大の長方形」を見つけ、その面積を求めるのが本記事のテーマです。ポイントは、任意の2つの列を入れ替えてよいという条件がある点です。 例として、次の行列を見てみましょう。 10010 10011 11010 この場合、答えは 6 です。1列目と3列目を入れ替えると、次のように3列目と4列目がすべて1になり、高さ3・幅2の長方形(面積 3 × 2 = 6)が現れます。 00110 00111 01110 解法の考え方 列を自由に入れ替えられるということは、ある行を底辺としたとき、その行の各列の「高
-
Pythonで文字列から辞書式順序で最大の回文部分列を見つける方法
問題の概要文字列Sが与えられたとき、その文字列から辞書式順序で最大の回文(パリンドローム)部分列を見つけることを考えます。例えば、入力が「tutorialspointtutorial」の場合、出力は「uu」となります。解決のアプローチこの問題は一見複雑に思えますが、実は非常にシンプルな性質を利用することで効率的に解けます。その鍵となるのは、辞書式順序で最大の回文部分列は、文字列に含まれる最大の文字だけで構成されるという点です。理由は以下の通りです。任意の1文字は、それ自体が回文です。同じ文字を繰り返した文字列も、必ず回文になります。したがって、文字列中の最大文字をすべて集めたものが、辞書式順序
-
Pythonで指定された条件を満たす辞書順最小の文字列を見つける方法
問題の概要 長さnの整数配列Aが与えられます。A[i]は、ある文字列sの先頭から(i+1)文字分の接頭辞に含まれる異なる文字の種類数を表します。このとき、与えられた配列Aの条件をすべて満たす辞書順最小の文字列を見つける必要があります。使用できる文字は小文字の英字[a-z]のみで、条件を満たす文字列が存在しない場合は-1を返します。 例えば、入力が A = [1, 1, 2, 3, 4] の場合、出力は「aabcd」になります。各接頭辞における異なる文字数は以下の通りです。 prefix[0](a): 1種類 prefix[1](aa): 1種類 prefix[2](aab): 2種類 pre
-
【Python】接頭辞・接尾辞・部分文字列のすべてに該当する最長のサブ文字列を検索する方法
与えられた文字列の中から、「接頭辞(プレフィックス)」かつ「接尾辞(サフィックス)」であり、さらに文字列の途中にも部分文字列として現れる最長のサブ文字列を見つける問題を考えてみましょう。該当するサブ文字列が存在しない場合は -1 を返します。 例えば、入力が languagepythonlanguageinterestinglanguage の場合、先頭・末尾・そして文字列の途中にも現れる language が答えとなります。 解決のアプローチ:LPS配列を活用する この問題は、KMP法(Knuth–Morris–Pratt法)でも使われる「LPS配列」(各位置における「最長の接頭辞かつ接尾辞
-
PythonでLCMがK以下となる最長部分列(サブシーケンス)を見つける方法
問題の概要互いに異なる n 個の数値からなる配列 A と、正整数 K が与えられたとします。このとき、最小公倍数(LCM)が K 以下となる最長の部分列(サブシーケンス)を配列から見つけます。条件を満たす部分列が存在する場合は、その LCM の値・部分列の長さ・要素のインデックス(0始まり)を出力し、存在しない場合は -1 を返します。例として、入力が A = [3, 4, 5, 6]、K = 20 の場合を考えてみましょう。このとき出力は次のようになります。LCM = 12長さ = 3インデックス = [0, 1, 3]これは、配列の 0 番目・1 番目・3 番目にある「3, 4, 6」を選
-
Pythonでk種類の一意な文字を含む最長部分文字列を求める方法【スライディングウィンドウ法】
問題の概要 文字列が与えられたとき、ちょうどk個の一意な(重複しない)文字を含む最長の部分文字列を返すことを考えます。条件を満たす最長の部分文字列が複数存在する場合は、そのうちのどれか1つを返せば問題ありません。 例えば、入力が s = ppqprqtqtqt、k = 3 の場合、出力は長さ7の「rqtqtqt」となります。 解法の考え方:スライディングウィンドウ法 この問題はスライディングウィンドウ(尺取り法)と呼ばれる手法で効率的に解けます。ウィンドウの右端を1文字ずつ伸ばしていき、一意な文字の種類数が制約を超えたら左端を縮める、という操作を繰り返すことで答えを求めます。 アルゴリズム
-
Pythonでn台のバイクが走行できる最大距離を求めるアルゴリズム
問題概要 n台のバイクがあり、それぞれ満タンの状態で100km走行できるものとします。このn台を使って到達できる最大距離を求めるのが本記事の目的です。なお、ここではすべてのバイクが同一仕様であり、1km走行するのに1リットルの燃料を消費すると仮定します。 もしn台すべてが同じ地点から並走した場合、走行できる距離は100kmにとどまってしまいます。そこで目標となるのは、燃料の無駄を最小限に抑えながら最大距離を走ることです。燃料の浪費を減らすということは、言い換えれば実際に動かすバイクの台数を最小化することを意味します。 解決のアプローチ:燃料の中継ぎ戦略 バイクを直列的に運用すれば、より遠くまで
-
Pythonで数値を合成数の和に分解したときの最大項数を求める方法
整数 N(1 ≤ N ≤ 10^9)が与えられたとき、N をできるだけ多くの合成数(composite number)の和として表現し、その最大の項数を返すことを考えます。もし分解が不可能な場合は -1 を返します。例えば、入力が 16 の場合、出力は 4 になります。16 は 4 + 4 + 4 + 4 とも 8 + 8 とも表せますが、項数が最大になるのは 4 + 4 + 4 + 4 の4項構成だからです。解法のアプローチこの問題は動的計画法(DP)を使うことで効率的に解けます。ポイントは次のとおりです。最小の合成数は 4、続いて 6、9 です。実はすべての合成数は 4・6・9 の組み合わ
-
PythonでO(n)時間・O(1)の追加メモリを使って最大出現回数の数値を見つける方法
問題の概要サイズ n の配列が与えられ、その要素はすべて 0 から k−1 の範囲に含まれているとします。ここで k は正の整数であり、k ≤ n を満たすものとします。この条件のもとで、配列の中で最も多く出現する数値(最大繰り返し数)を見つけることが課題です。たとえば、k = 8、A = [3, 4, 4, 6, 4, 5, 2, 8] という入力が与えられた場合、4 は3回出現して最も多いため、出力は 4 となります。アルゴリズムの考え方この問題は、ハッシュマップやカウンタ用の追加配列を使わずに解くことができます。ポイントは「各要素の値が必ず k 未満である」という制約を利用することです。
-
PythonでN種類すべてのキャンディを購入する際の最小金額と最大金額を求める方法
問題の概要 あるお菓子屋さんでは、N種類のキャンディが販売されており、それぞれの価格が与えられています。この店では魅力的なキャンペーンを実施していて、キャンディを1つ購入すると、別の種類のキャンディを最大K個まで無料でもらえるというオファーがあります。 今回の課題は、N種類すべてのキャンディを買い揃えるために必要となる最小の支払金額と最大の支払金額をそれぞれ求めることです。どちらの場合も、必ずこのオファーを活用して、できるだけ多くのキャンディを無料で手に入れるものとします。残っているキャンディがK個以上ある場合は、1つ購入するごとに必ずK個を受け取ります。K個未満しか残っていない場合は、残りを
-
Pythonでシフト後の2つの数表間の最小差を求める方法
```html 問題の概要 2つの数 p と q が与えられたとき、それぞれの数が持つ無限に続く倍数の表(九九の表)を考えます。これらの表をそれぞれ r と s(ただし r, s >= 0)だけシフトした場合、2つのシフト済み表の項同士における最小の差を求めるのが本記事のテーマです。 例として、p = 7、q = 17、r = 6、s = 3 の場合の出力は 0 になります。 7の表:[7, 14, 21, 28, 35, 42, 49, ...] 17の表:[17, 34, 51, 68, 85, 102, 119, ...] 7の表を6シフトした表:[13, 20, 27, 34,
-
【Python】配列全体をソートするために必要な最小長の未ソート部分配列を見つける方法
問題の概要サイズ n のソートされていない配列 A[0..n-1] が与えられたとします。このとき、その部分配列だけをソートすれば配列全体が整った状態になるような、最小の長さを持つ部分配列 A[s..e] を見つける必要があります。例えば、配列が [2,6,4,8,10,9,15] の場合、答えは 5 となり、該当する部分配列は [6,4,8,10,9] です。この部分配列を昇順に並び替えると、配列全体が完全にソートされます。解決のアプローチこの問題は、元の配列と「ソート済みの配列」を比較することで解けます。具体的には、以下の手順に従います。元の配列 nums をソートした結果を res とし
-
【Python】行列内のスタートセルからゴールセルまでの最小移動回数をBFSで求める方法
問題概要 N×N の行列 M があり、各セルには「1」「0」「2」「3」のいずれかの値が格納されています。この行列の中で、スタート地点(ソースセル)からゴール地点(デスティネーションセル)まで移動する際に必要となる最小移動回数を求めます。移動は空白セルのみを経由して行うことができ、上下左右の4方向に1マスずつ進むことができます。 1 … スタート地点(ソース)のセル 2 … ゴール地点(デスティネーション)のセル 3 … 移動可能な空白セル 0 … 壁(通過不可) スタートとゴールはそれぞれ必ず1つだけ存在し、スタートからゴールへの経路は複数存在する場合があります。行列上での1回の移動を「
-
【Python】2つの文字列を一致させるために必要な前処理の最小移動回数を求める方法
問題の概要同じ長さを持ち、小文字の英字のみからなる2つの文字列 P と Q が与えられます。次に示す操作を適用した後、P を Q と完全に一致させるために、事前に P に施すべき前処理(文字の置き換え)の最小回数を求めます。任意のインデックス i を選び、文字 p[i] と q[i] を入れ替える。任意のインデックス i を選び、文字 p[i] と p[n − i − 1] を入れ替える。任意のインデックス i を選び、文字 q[i] と q[n − i − 1] を入れ替える。注: インデックス i の範囲は 0 ≤ i < n です。また、1回の前処理では、P 内の任意の1文字を英語
-
Pythonでk回のジャンプで最後の島に到達する:ジャンプ最大距離の最小値を二分探索で求める
問題概要数列 A が与えられ、A の i 番目の要素は i 番目の島の位置を表しているとします。さらに整数 k(1 ≤ k < N)も与えられます。ここで、ある人が 0 番目の島からスタートし、ちょうど k 回のジャンプで最後の島に到達しなければなりません。その際、移動中に行う「1 回のジャンプの長さ」の最大値が最小になるようにしたとき、その値を求めるのがこの問題です。なお、すべての島の位置は昇順に並んでいるものとします。入力例と出力例たとえば、入力が A = [7, 20, 41, 48]、k = 2 の場合、出力は 28 になります。理由を見てみましょう。経路 1:7 → 20 →