Python
 Computer >> コンピューター >  >> プログラミング >> Python

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 の末尾に挿入する
    • 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) となります。グラフの規模が大きくなっても効率よく動作する、実用的な手法と言えるでしょう。

  1. Pythonで最長アナグラム部分列の長さを求めるプログラム

    問題の概要小文字のみで構成された2つの文字列 S と T が与えられたとき、「最も長いアナグラム部分列」の長さを求めます。ここでアナグラム部分列とは、両方の文字列に共通して含まれる文字を組み合わせて作れる、同じ文字構成を持つ部分列のことです。例えば、S = helloworld、T = hellorld の場合、答えは 8 になります。これは、両方の文字列で共有できる文字(h ×1、e ×1、l ×3、o ×1、r ×1、d ×1)の合計が8文字であるためです。解法のアプローチこの問題は、各文字列における文字の出現回数を数え、その最小値を合計することで効率的に解けます。手順は以下の通りです。文

  2. Pythonで最長のバランス括弧部分列の長さを求めるプログラム

    問題概要 文字列 s が与えられます。この文字列には括弧「(」と「)」が含まれており、その中からバランスの取れた(対応関係が成立している)括弧の部分列として最も長いものを見つけ、その長さを返すことが目標です。 たとえば、入力が s = ())(()( の場合、出力は 4 になります。「(」と「)」を選び抜いて ()() というバランスの取れた部分列を作れるためです。 解法のアプローチ この問題は、文字列を後ろから走査することで線形時間で解けます。閉じ括弧を先に確保しておき、開き括弧が出てきたときに対を成立させるという発想です。手順は以下の通りです。 結果を格納する変数 res を 0 で初