Pythonでターゲットノードを含む最短サイクルの長さを求める方法(BFS活用)
問題の概要
有向グラフの隣接リストが与えられます。各インデックス i のリストには、ノード i から直接接続されているノードの一覧が格納されています。さらに、探索対象となる値(target)も与えられます。この課題では、target を含むサイクル(閉路)の中で最も短いものの長さを求めます。該当するサイクルが存在しない場合は -1 を返してください。
具体例
例えば、次のようなグラフが与えられたとします。
graph = [[1, 4], [2], [3], [0, 1], []]
target = 3 の場合、出力は 3 になります。これは、ノード 1 → 2 → 3 → 1 というサイクルが存在するためです。なお、0 → 1 → 2 → 3 → 0 という別のサイクルもありますが、こちらの方が長いため最短としては採用されません。
解き方のアプローチ
この問題は幅優先探索(BFS)を使うことで効率的に解けます。target ノードから出発してグラフを段階的に探索し、再び target に戻ってきた時点の深さ(ステップ数)が、そのまま最短サイクルの長さになります。
具体的な手順は以下の通りです。
- visited := 訪問済みノードを記録する新しい集合
- l := target を要素に持つリスト(現在の探索層)
- length := 0(サイクルの長さ)
- l が空でない間、以下を繰り返す:
- length を 1 増やす
- nl := 次の探索層を格納する新しいリスト
- l 内の各ノード u について:
- graph[u] 内の各ノード v について:
- v が target と一致したら、length を返す(サイクル発見)
- v が訪問済みの場合はスキップして次へ進む
- v を visited に追加し、nl の末尾に挿入する
- graph[u] 内の各ノード v について:
- l := nl(探索層を次に進める)
- ループが終了しても見つからなければ、-1 を返す
ポイントは、visited 集合で一度訪れたノードを二度と探索しないようにすることです。これにより無駄な探索が省かれ、最初に target へ戻ってきた経路が必ず最短になります。
Pythonでの実装例
それでは、上記の手順を実装してみましょう。
class Solution:
def solve(self, graph, target):
visited = set()
l = [target]
length = 0
while l:
length += 1
nl = []
for u in l:
for v in graph[u]:
if v == target:
return length
if v in visited:
continue
visited.add(v)
nl.append(v)
l = nl
return -1
ob = Solution()
graph = [[1, 4], [2], [3], [0, 1], []]
target = 3
print(ob.solve(graph, target))入力
[[1, 4], [2], [3], [0, 1], []]
出力
3
計算量の評価
このアルゴリズムは、各ノードと各エッジをそれぞれ最大1回ずつ処理するため、時間計算量は O(V + E)(Vはノード数、Eはエッジ数)です。また、訪問済み集合と探索キューに必要なメモリから、空間計算量は O(V) となります。グラフの規模が大きくなっても効率よく動作する、実用的な手法と言えるでしょう。
-
Pythonで最長アナグラム部分列の長さを求めるプログラム
問題の概要小文字のみで構成された2つの文字列 S と T が与えられたとき、「最も長いアナグラム部分列」の長さを求めます。ここでアナグラム部分列とは、両方の文字列に共通して含まれる文字を組み合わせて作れる、同じ文字構成を持つ部分列のことです。例えば、S = helloworld、T = hellorld の場合、答えは 8 になります。これは、両方の文字列で共有できる文字(h ×1、e ×1、l ×3、o ×1、r ×1、d ×1)の合計が8文字であるためです。解法のアプローチこの問題は、各文字列における文字の出現回数を数え、その最小値を合計することで効率的に解けます。手順は以下の通りです。文
-
Pythonで最長のバランス括弧部分列の長さを求めるプログラム
問題概要 文字列 s が与えられます。この文字列には括弧「(」と「)」が含まれており、その中からバランスの取れた(対応関係が成立している)括弧の部分列として最も長いものを見つけ、その長さを返すことが目標です。 たとえば、入力が s = ())(()( の場合、出力は 4 になります。「(」と「)」を選び抜いて ()() というバランスの取れた部分列を作れるためです。 解法のアプローチ この問題は、文字列を後ろから走査することで線形時間で解けます。閉じ括弧を先に確保しておき、開き括弧が出てきたときに対を成立させるという発想です。手順は以下の通りです。 結果を格納する変数 res を 0 で初