Keito

© 2024 Keito

技術ブログとポートフォリオ

最長共通部分列(LCS)

動的計画法で二つの文字列の最長共通部分列を効率的に求める文字列アルゴリズム

O(m×n)
時間計算量
O(m×n)
空間計算量
中級
難易度
文字列DP
2次元テーブル

📝 文字列入力

比較対象:
ABCDGH
AEDFHR
テーブルサイズ:
7 × 7 = 49 セル
🔤 部分列:元の順序を保ったまま文字を抜き出したもの
6/10 文字(英数字のみ)
6/10 文字(英数字のみ)

📚 推奨入力例

🧮

最長共通部分列を計算してください

左側の入力パネルから文字列を設定し、「LCS計算実行」ボタンを押してください

💻 実装例(JavaScript)

// 最長共通部分列(LCS)を動的計画法で求める
function lcs(str1, str2) {
    const m = str1.length;
    const n = str2.length;
    
    // DPテーブルを初期化(0で埋める)
    const dp = Array(m + 1).fill(null)
        .map(() => Array(n + 1).fill(0));
    
    // DPテーブルを構築
    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            if (str1[i - 1] === str2[j - 1]) {
                // 文字が一致:斜め上の値 + 1
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                // 文字が不一致:上または左の最大値
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    
    // バックトラックでLCSを構築
    const lcsArray = [];
    let i = m, j = n;
    
    while (i > 0 && j > 0) {
        if (str1[i - 1] === str2[j - 1]) {
            // 文字が一致:LCSに追加して斜め上に移動
            lcsArray.unshift(str1[i - 1]);
            i--;
            j--;
        } else if (dp[i - 1][j] >= dp[i][j - 1]) {
            // 上の値が大きい(または同じ):上に移動
            i--;
        } else {
            // 左の値が大きい:左に移動
            j--;
        }
    }
    
    return {
        length: dp[m][n],
        lcs: lcsArray.join('')
    };
}

// 使用例
console.log(lcs("ABCDGH", "AEDFHR"));    // { length: 3, lcs: "ADH" }
console.log(lcs("AGGTAB", "GXTXAYB"));   // { length: 4, lcs: "GTAB" }
console.log(lcs("ABC", "ABC"));          // { length: 3, lcs: "ABC" }
console.log(lcs("ABC", "DEF"));          // { length: 0, lcs: "" }

// DNA配列解析での応用例
function dnaSequenceAlignment(seq1, seq2) {
    const result = lcs(seq1, seq2);
    const similarity = (result.length / Math.max(seq1.length, seq2.length)) * 100;
    
    return {
        ...result,
        similarity: similarity.toFixed(2) + "%"
    };
}

console.log(dnaSequenceAlignment("ATCGATCG", "ATGCATCG"));
// { length: 7, lcs: "ATCATCG", similarity: "87.50%" }

// 複数文字列のLCS(再帰的適用)
function lcsMultiple(strings) {
    return strings.reduce((acc, str) => lcs(acc, str).lcs);
}

🎯 アルゴリズムの特徴

動的計画法の特性

  • • 2次元DPテーブルによる効率的計算
  • • 部分列 ≠ 部分文字列(連続不要)
  • • O(m×n)で全探索O(2^n)より大幅高速
  • • バックトラックで実際の部分列構築

実世界での応用

  • • DNAシーケンス解析・遺伝子比較
  • • テキストの差分検出(diff, git)
  • • バージョン管理システム
  • • 文書の類似度判定

💡 学習ポイント: LCSは動的計画法と文字列処理の代表的な組み合わせで、 バイオインフォマティクス分野で特に重要な役割を果たします。