動的計画法による最長共通部分列と最大部分和の解法

718. 最長共通部分配列の探索

2つの整数配列が与えられた場合、最長の共通部分配列の長さを求める問題です。部分配列は連続する要素から構成され、相対的な順序を保持する必要があります。

例:

入力:
配列A: [5, 8, 3, 7, 9]
配列B: [3, 7, 9, 4, 6]
出力:3
説明:最長共通部分配列は[3, 7, 9]

解法アプローチ

2次元DPテーブルを使用します。cache[i][j]は、配列Aの0~i-1番目と配列Bの0~j-1番目の要素における最長共通部分配列の長さを表します。

状態遷移式:

  • 要素が一致する場合:cache[i][j] = cache[i-1][j-1] + 1
  • 一致しない場合:cache[i][j] = 0(連続性が途切れるため)

最適化実装

def find_max_common_subarray(arr1: list, arr2: list) -> int:
    len_a, len_b = len(arr1), len(arr2)
    cache = [[0] * (len_b + 1) for _ in range(len_a + 1)]
    max_length = 0
    
    for i in range(1, len_a + 1):
        for j in range(1, len_b + 1):
            if arr1[i-1] == arr2[j-1]:
                cache[i][j] = cache[i-1][j-1] + 1
                max_length = max(max_length, cache[i][j])
    return max_length

1143. 最長共通部分列の計算

2つの文字列の最長共通部分列(LCS)の長さを求める問題です。部分列は連続する必要はなく、相対的な順序を保持する必要があります。

例:

入力:
text1 = "programming"
text2 = "pragmatic"
出力:6
説明:最長共通部分列は"pragam"

状態遷移ロジック

cache[i][j]をtext1のi文字目とtext2のj文字目までのLCS長さと定義します。

遷移条件:

  • 文字が一致:cache[i][j] = cache[i-1][j-1] + 1
  • 不一致:cache[i][j] = max(cache[i-1][j], cache[i][j-1])

効率的な実装

def compute_lcs(str1: str, str2: str) -> int:
    m, n = len(str1), len(str2)
    prev_row = [0] * (n + 1)
    
    for i in range(1, m + 1):
        current_row = [0]
        for j in range(1, n + 1):
            if str1[i-1] == str2[j-1]:
                current_row.append(prev_row[j-1] + 1)
            else:
                current_row.append(max(prev_row[j], current_row[-1]))
        prev_row = current_row
    return prev_row[-1]

53. 最大部分和の最適化

整数配列から連続する部分配列の最大和を求める問題です。Kadaneのアルゴリズムを応用した解法が有効です。

例:

入力: [-3, 4, -1, 2, 1, -5, 4]
出力: 6
説明:部分配列[4, -1, 2, 1]の和が最大

動的計画法の適用

current_sumを現在の位置で終わる最大部分和と定義します。

更新ルール:

current_sum = max(前回のcurrent_sum + 現在の要素, 現在の要素)

空間効率の良い実装

def find_max_subarray_sum(nums: list) -> int:
    if not nums:
        return 0
        
    global_max = current_max = nums[0]
    
    for num in nums[1:]:
        current_max = max(num, current_max + num)
        global_max = max(global_max, current_max)
        
    return global_max

1035. 交差しない接続線の最大化

2つの配列間で、値が等しく交差しない線を最大限に引く問題です。本質的には最長共通部分列問題と同等です。

解法の本質

線が交差しないという制約は、要素の相対的な順序を保持する必要があることを意味します。したがって、LCSの長さがそのまま最大接続数となります。

統合的実装

def max_non_crossing_lines(seq1: list, seq2: list) -> int:
    dp_table = [[0] * (len(seq2) + 1) for _ in range(len(seq1) + 1)]
    
    for i in range(len(seq1)):
        for j in range(len(seq2)):
            if seq1[i] == seq2[j]:
                dp_table[i+1][j+1] = dp_table[i][j] + 1
            else:
                dp_table[i+1][j+1] = max(dp_table[i][j+1], dp_table[i+1][j])
                
    return dp_table[-1][-1]

タグ: 動的計画法 最長共通部分列 最大部分和 Python アルゴリズム最適化

6月21日 16:32 投稿