令和7年度 秋期 応用情報技術者試験 午後 問題 問3 動的計画法による最長共通部分列

テクノロジアルゴリズム

この問題は2025(R7)秋 応用情報技術者 午後に出題されたものです。出題時点の法令・制度に基づく内容のため、現行の内容と一致しない場合があります。

本ページの問題文・選択肢は、原本の体裁を Web 表示用に正規化しています(改行・記号・数式・図表参照の調整)。設問の趣旨および正解に影響する変更は加えていません。

学習ガイド

最長共通部分列(LCS)問題を動的計画法で解くアルゴリズムの問題です。部分列の定義確認から始まり、DPテーブルの値の計算、プログラムの空欄補充、そして計算量の考察まで、動的計画法の学習に必要な要素が一通り揃っています。正答率は高めでしたが、漸化式の分岐(文字が一致する場合としない場合)を曖昧に覚えていると表の値を取り違えます。この記事ではテーブルを実際に埋めながら、コードとの対応を固めます。

この記事で押さえる論点

  • LCSの定義(部分列・共通部分列)を例で確認する
  • 表(DPテーブル)の値がどの漸化式で埋まるかを説明する
  • プログラムの空欄を漸化式との対応から導く

問題本文

二つの列の最長共通部分列 (Longest Common Subsequence) の長さを求めるアルゴリズムに関する次の記述を読んで,設問に答えよ。

X={x1,x2,,xn}X=\{x_1, x_2, \dots, x_n\} に対して,順序を保持して要素を抽出した列を部分列という。また,列の長さはその列の要素の個数で定義される。ここでは,各要素が文字である列を考える。例えば,X={“A”,“B”,“C”,“B”,“D”,“A”,“B”}X=\{\text{“A”}, \text{“B”}, \text{“C”}, \text{“B”}, \text{“D”}, \text{“A”}, \text{“B”}\} のとき,図1に示すように {“B”,“C”,“D”,“B”}\{\text{“B”}, \text{“C”}, \text{“D”}, \text{“B”}\}XX の部分列の例であり,その長さは4である。

図1 部分列の例
図の説明テキスト

列Xの各要素「A, B, C, B, D, A, B」が四角で囲まれて横一列に並んでいる。そこから部分列の例として抽出された要素「B, C, D, B」へ矢印が引かれている。矢印は、Xの1つ目のBから部分列のB、CからC、DからD、2つ目(末尾)のBから部分列の最後のBへ伸びている。抽出された列の左側には「Xの部分列の例 (長さは4)」というテキストが添えられている。

ある列 ZZ が二つの列 X,YX, Y 両方の部分列であるとき,ZZXXYY との共通部分列といい,共通部分列のうち長さが最大となるものを最長共通部分列という。最長共通部分列は複数通り存在する場合もあるが,その長さは一意に決まる。X={“A”,“B”,“C”,“B”,“D”,“A”,“B”},Y={“B”,“D”,“C”,“A”,“B”,“A”}X=\{\text{“A”}, \text{“B”}, \text{“C”}, \text{“B”}, \text{“D”}, \text{“A”}, \text{“B”}\}, Y=\{\text{“B”}, \text{“D”}, \text{“C”}, \text{“A”}, \text{“B”}, \text{“A”}\} の場合の共通部分列及び最長共通部分列の例を図2に示す。

図2 共通部分列及び最長共通部分列の例
図の説明テキスト

上段に列X「A, B, C, B, D, A, B」と列Y「B, D, C, A, B, A」が示されている。その下部に、XとYの共通部分列の例として以下の3つの列が挙げられている。

  1. 「B, C, A」
  2. 「B, C, B, A」
  3. 「B, D, A, B」
    これら3つの列は左側で「X, Yの共通部分列の例」というラベルで中括弧によりまとめられている。また、2つ目の列と3つ目の列は右側で「X, Yの最長共通部分列の例 (最長共通部分列の長さは4)」というラベルで中括弧によりまとめられている。

なお,共通部分列が存在しない場合,最長共通部分列は空の列となり,その長さは0である。
最長共通部分列の長さは,2本のDNAの塩基配列間の類似度を測る目的などに用いられる。

〔最長共通部分列の長さを求めるアルゴリズム〕

二つの列 X,YX, Y の最長共通部分列の長さを求めるアルゴリズムを考える。列 X,YX, Y それぞれについて,先頭から nn 個,kk 個の要素を抽出した列を Xn,YkX_n, Y_k と表記し,列 Xn,YkX_n, Y_k それぞれの末尾の要素を xn,ykx_n, y_k と表記する。例えば,X={“A”,“B”,“C”,“B”}X=\{\text{“A”}, \text{“B”}, \text{“C”}, \text{“B”}\} とすると,X3={“A”,“B”,“C”},x3=“C”X_3=\{\text{“A”}, \text{“B”}, \text{“C”}\}, x_3=\text{“C”} である。このとき,列 XnX_n と列 YkY_k との最長共通部分列の中の一つを LCS(n,k)LCS(n, k),最長共通部分列の長さを LCSL(n,k)LCSL(n, k) と表記する。なお,X0X_0Y0Y_0 は空の列であり,x0x_0y0y_0 は存在しない。

ここで,xnx_nyky_k とが一致しているか否かに着目して次の1.〜3.に場合分けし,再帰的な関係を用いて LCSL(n,k)LCSL(n, k) を求めることを考える。

  1. xn=ykx_n=y_k の場合を考える。例えば,xn=yk=“A”x_n=y_k=\text{“A”} とする。このとき,LCS(n,k)LCS(n, k) の末尾の要素は “A” となる。よって,LCS(n,k)LCS(n, k) は,列 Xn1,Yk1X_{n-1}, Y_{k-1} の最長共通部分列 LCS(n1,k1)LCS(n-1, k-1) の末尾に “A” を付加したものと一致する。したがって,LCSL(n,k)=LCSL(n1,k1)+1LCSL(n, k) = LCSL(n-1, k-1) + 1 が成り立つ。
  2. xnykx_n \neq y_k の場合を考える。例えば,xn=“A”,yk=“B”x_n=\text{“A”}, y_k=\text{“B”} とする。ここで,LCS(n,k)LCS(n, k) の末尾の要素は “A” 又は “A” 以外となる。LCS(n,k)LCS(n, k) の末尾の要素が “A” である場合は,列 YkY_k から末尾の “B” を取り除いても最長共通部分列には影響しないので,LCS(n,k)LCS(n, k)LCS(n,k1)LCS(n, k-1) と一致する。一方,LCS(n,k)LCS(n, k) の末尾の要素が “A” でない場合は,列 XnX_n から末尾の “A” を取り除いても最長共通部分列には影響しないので,LCS(n,k)LCS(n, k)LCS(n1,k)LCS(n-1, k) と一致する。よって,LCS(n,k)LCS(n, k)LCS(n,k1)LCS(n, k-1) 又は LCS(n1,k)LCS(n-1, k) のいずれかと一致する。したがって,LCSL(n,k)LCSL(n, k) は,LCSL(n,k1)LCSL(n, k-1)LCSL(n1,k)LCSL(n-1, k) のうちの最大値と一致する。
  3. n=0n=0 又は k=0k=0 の場合,最長共通部分列は空の列となり,LCSL(n,k)=0LCSL(n, k)=0 である。

〔動的計画法を用いて最長共通部分列の長さを求めるアルゴリズム〕

1.〜3.の再帰的な関係に従い,LCSL(n,k)LCSL(n, k) を再帰的に計算することによって,列 X,YX, Y の最長共通部分列の長さを求めることができる。しかし,再帰的に計算するアルゴリズムでは,重複して同じ計算をすることによって時間計算量が大きくなり,非効率になる場合がある。そこで,重複して同じ計算をすることを避けるために,LCSL(n,k)LCSL(n, k) の値を動的計画法によって求めることを考える。

二つの列が X={“A”,“B”,“C”,“B”,“D”,“A”,“B”},Y={“B”,“D”,“C”,“A”,“B”,“A”}X=\{\text{“A”}, \text{“B”}, \text{“C”}, \text{“B”}, \text{“D”}, \text{“A”}, \text{“B”}\}, Y=\{\text{“B”}, \text{“D”}, \text{“C”}, \text{“A”}, \text{“B”}, \text{“A”}\} の場合,0n70 \le n \le 7 かつ 0k60 \le k \le 6 に対する LCSL(n,k)LCSL(n, k) の値を図3に示す。図3の左端2列は nn (0〜7)とそれに対応する xnx_n を,上端2行は kk (0〜6)とそれに対応する yky_k を表す。n=0n=0 のときの xnx_nk=0k=0 のときの yky_k は存在しないので,“-”と表す。各要素は列 XnX_n と列 YkY_k との最長共通部分列の長さ LCSL(n,k)LCSL(n, k) の値を示している。

図3 LCSL(n, k)の値
図の説明テキスト

LCSL(n, k)の値を示す表。
表の左端2列はnと対応するx_n、上端2行はkと対応するy_kを示し、各セルにLCSL(n, k)の値が記載されている。

n \ k x_n \ y_k 0 / - 1 / B 2 / D 3 / C 4 / A 5 / B 6 / A
0 - 0 0 0 0 0 0 0
1 A 0 0 0
2 B 0 1 (①) 1 (②)
3 C 0 1 1
4 B 0 1 1
5 D 0 1 2
6 A 0 1 2 (③)
7 B 0 1

注記1 ( ) 内の①〜③については,図3に続く本文で値の求め方を説明している。
注記2 LCSL(n, k)の値の一部は,設問のため表示していない。

図3の各要素の値は,〔最長共通部分列の長さを求めるアルゴリズム〕の1.〜3.の再帰的な関係に従って求められる。
まず,n=0n=0 又は k=0k=0 のときは3.に対応するので,LCSL(n,k)=0LCSL(n, k)=0 である。
それ以外の値について,例えば,図3の①〜③は次のように値が決まる。

  • ①について,x2=y1x_2=y_1 なので1.に対応し,LCSL(2,1)=LCSL(1,0)+1LCSL(2, 1) = LCSL(1, 0) + 1 である。LCSL(1,0)=0LCSL(1, 0)=0 なので,LCSL(2,1)=1LCSL(2, 1)=1 となる。
  • ②について,x2y2x_2 \neq y_2 なので2.に対応し,LCSL(2,1)=1,LCSL(1,2)=0LCSL(2, 1)=1, LCSL(1, 2)=0 なので,LCSL(2,2)=1LCSL(2, 2)=1 となる。
  • ③について,x6y2x_6 \neq y_2 なので2.に対応し,LCSL(6,1)=1,LCSL(5,2)=2LCSL(6, 1)=1, LCSL(5, 2)=2 なので,LCSL(6,2)=2LCSL(6, 2)=2 となる。

図3の要素の値を全て計算することによって列 X,YX, Y の最長共通部分列の長さが4であると分かる。

〔動的計画法を用いて最長共通部分列の長さを求めるプログラム〕

〔動的計画法を用いて最長共通部分列の長さを求めるアルゴリズム〕に基づいて,二つの列の最長共通部分列の長さを求めるプログラムを考える。任意の二つの列をそれぞれ配列 ST として受け取り,動的計画法を用いて最長共通部分列の長さを求めるプログラムを図4に示す。ここで,配列の要素番号は0から始まり,整数型の二次元配列 lcsl は,行番号が0から配列 S の要素数 ss までの (s+1)(s + 1) 行,列番号が0から配列 T の要素数 tt までの (t+1)(t + 1) 列の大きさをもつ。

図4 動的計画法を用いて最長共通部分列の長さを求めるプログラム
図の説明テキスト

擬似言語で記述されたプログラムのコード。

○整数型: calculate_lcsl(文字型の配列: S, 文字型の配列: T)
整数型: s ← Sの要素数
整数型: t ← Tの要素数
整数型の二次元配列: lcsl ← {(s + 1)行, (t + 1)列の未定義の値}
整数型: n, k
for (nを0からsまで1ずつ増やす)
  lcsl[n, 0] ← 0
endfor
for (kを0からtまで1ずつ増やす)
  lcsl[0, k] ← 0
endfor
for (nを1からsまで1ずつ増やす)
  for (kを1からtまで1ずつ増やす)
    if (S[n - 1]がT[k - 1]と等しい)
      lcsl[n, k] ← <span id="q3_blank_u" class="blank-label">ウ</span>
    elseif (lcsl[n, k - 1]がlcsl[n - 1, k]より大きい)
      lcsl[n, k] ← <span id="q3_blank_e" class="blank-label">エ</span>
    else
      lcsl[n, k] ← <span id="q3_blank_o" class="blank-label">オ</span>
    endif
  endfor
endfor
return <span id="q3_blank_ka" class="blank-label">カ</span>

図4のプログラムの時間計算量を,配列 S の要素数 ss,配列 T の要素数 tt を用いて表すと O(O()) である。

設問と解答・解説

設問1

{“A”,“C”,“B”,“C”,“D”,“C”}\{\text{“A”}, \text{“C”}, \text{“B”}, \text{“C”}, \text{“D”}, \text{“C”}\} と列 {“C”,“D”,“B”,“D”,“C”,“A”}\{\text{“C”}, \text{“D”}, \text{“B”}, \text{“D”}, \text{“C”}, \text{“A”}\} との最長共通部分列の長さを答えよ。

模範解答

4

配点 3

解説

正解の根拠

{“A”,“C”,“B”,“C”,“D”,“C”}\{\text{“A”}, \text{“C”}, \text{“B”}, \text{“C”}, \text{“D”}, \text{“C”}\} と列 {“C”,“D”,“B”,“D”,“C”,“A”}\{\text{“C”}, \text{“D”}, \text{“B”}, \text{“D”}, \text{“C”}, \text{“A”}\} の最長共通部分列(LCS: Longest Common Subsequence)を求めます。

要素を順に比較し、両方の列に共通する部分列の中で最長のものを探します。
例えば、両方の列に含まれる部分列として {“C”,“B”,“D”,“C”}\{\text{“C”}, \text{“B”}, \text{“D”}, \text{“C”}\} が考えられます。

  • 1つ目の列: “A”, “C”, “B”, “C”, “D”, “C”
  • 2つ目の列: “C”, “D”, “B”, “D”, “C”, “A”

この部分列の長さは 4 であり、これより長い共通部分列は存在しないため、正解は 4 となります。

各選択肢の解説

本設問は記述式(数値解答)のため選択肢はありませんが、よくある誤答パターンを解説します。

  • 誤答例(3以下): 部分列が元の列の中で連続していなければならない(部分文字列)と勘違いし、連続する要素のみをカウントしてしまう誤りです。
  • 誤答例(5以上): 要素の順序を無視して、単に両方に存在する文字の数を数え上げてしまう誤りです。最長共通部分列は元の順序を保って抽出する必要があります。

設問2

図3中の に入れる適切な数値を答えよ。

(1)

模範解答

2

配点 3

解説

正解の根拠

動的計画法を用いた最長共通部分列問題のアルゴリズムに従い、二次元配列(DPテーブル)を更新します。

DPテーブルの各セルは以下の規則に基づいて値が決定されます。

  1. 比較する2つの文字が一致する場合: 左上のセルの値に 11 を加える。
  2. 一致しない場合: 上のセルと左のセルのうち、大きい方の値を引き継ぐ。

この規則に従って表を埋め進めると、図中の に該当するセルの計算結果は 2 となります。

各選択肢の解説

本設問は数値解答のため選択肢はありませんが、よくある誤答パターンを解説します。

  • 誤答例(1や3など): 文字が一致しない場合に左上の値をそのまま引き継いでしまったり、加算するタイミングを間違えるなど、DPテーブルの更新規則の理解不足による計算ミスが考えられます。

(2)

模範解答

4

配点 3

解説

正解の根拠

と同様に、動的計画法の更新規則に従ってDPテーブルを計算していきます。

テーブルの右下に近づくにつれて、見つかった共通部分列の長さが蓄積されていきます。 に到達するまでに、最長共通部分列の長さを表す値が正しく更新されている必要があります。
規則に従って正しく表を埋めると、 に該当するセルの値は 4 となります。

各選択肢の解説

本設問は数値解答のため選択肢はありませんが、よくある誤答パターンを解説します。

  • 誤答例(3など): 途中のセルの計算で最大値を取り損ねたり、一致する文字を見落として +1+1 を忘れたりすることで、最終的な値が小さくなってしまうミスが考えられます。

設問3

図4中の に入れる適切な字句を答えよ。

(1)

模範解答

lcsl[n - 1, k - 1] + 1

採点基準(配点 2点)

知識・理解度(内容)(1点)

  • 1: 文字が一致した際の動的計画法の漸化式の意味を正しく理解し、直前の状態に1を加える処理として意味的に正しい値を記述している。
  • 0: 漸化式の理解が不十分であり、意味的に誤った状態を参照している。

論理性(構造)(1点)

  • 1: プログラムの配列アクセスとして論理的に正しい構文で記述できている。
  • 0: 構文が誤っている、または要素番号の指定が論理的に破綻している。

解説

正解の根拠

動的計画法による最長共通部分列アルゴリズムにおいて、対象となる2つの文字が一致した場合の処理を記述する部分です。

文字が一致した場合、それより1つ前のインデックスまでの最長共通部分列の長さに 11 を加えたものが新しい長さになります。
プログラム上で1つ前の状態は lcsl[n - 1, k - 1] に格納されているため、これに + 1 を行った lcsl[n - 1, k - 1] + 1 が正解となります。

高得点のポイント

  • 二次元配列 lcsl の添字が n - 1 および k - 1 と正しく指定されていること。
  • 文字が一致したことによるインクリメント(+ 1)が正しく表現されていること。

(2)

模範解答

lcsl[n, k - 1]

採点基準(配点 2点)

知識・理解度(内容)(1点)

  • 1: 文字が一致しない場合の漸化式を正しく理解し、比較対象となる適切な配列要素を記述している。
  • 0: 漸化式の理解が不十分であり、誤った要素を参照している。

論理性(構造)(1点)

  • 1: プログラムの文法や配列のインデックス指定として論理的に正しい形式で記述できている。
  • 0: 記述が論理的に破綻しているか、無効な記述である。

解説

正解の根拠

対象の文字が一致しない場合、DPテーブルの更新は、1つ上のセルまたは1つ左のセルのうち大きい方の値を引き継ぐ操作になります。

該当のプログラムのロジックでは、lcsl[n - 1, k]lcsl[n, k - 1] を比較するか、条件分岐で大きい方を代入します。
空欄 の組み合わせとして、一方の比較対象である lcsl[n, k - 1] が入ります。

高得点のポイント

  • 動的計画法において、文字が一致しなかった場合の漸化式を正しく理解していること。
  • 配列の要素番号の指定(nk - 1 など)が正確であること。

(3)

模範解答

lcsl[n - 1, k]

採点基準(配点 2点)

知識・理解度(内容)(1点)

  • 1: 文字が一致しない場合の漸化式を正しく理解し、比較対象となる適切な配列要素を記述している。
  • 0: 漸化式の理解が不十分であり、誤った要素を参照している。

論理性(構造)(1点)

  • 1: プログラムの文法や配列のインデックス指定として論理的に正しい形式で記述できている。
  • 0: 記述が論理的に破綻しているか、無効な記述である。

解説

正解の根拠

空欄 と同様に、文字が一致しない場合に引き継ぐ値を求めるため、もう一方の比較対象となるセルを指定します。

上のセルと左のセルを比較するため、一方が lcsl[n, k - 1] であれば、もう一方は lcsl[n - 1, k] となります。

高得点のポイント

  • 一致しない場合の比較対象である2つのセルのうち、正しい一方を記述できていること。
  • 配列 lcsl のインデックスの増減(n - 1k)を正確に表現できていること。

(4)

模範解答

lcsl[s, t]

採点基準(配点 2点)

知識・理解度(内容)(1点)

  • 1: ループ終了後に最終的な解が格納される配列の要素位置を正確に理解し記述している。
  • 0: 最終的な解が格納される位置の理解が不十分である。

論理性(構造)(1点)

  • 1: プログラムの戻り値として論理的に正しい構文で記述できている。
  • 0: 構文が誤っている、またはインデックス指定が論理的に破綻している。

解説

正解の根拠

二重ループによるDPテーブルの更新がすべて終了した後、求める「最長共通部分列の長さ」は、テーブルの最も右下の要素に格納されています。

2つの列の長さがそれぞれ sstt であるため、最終結果が格納されている配列の要素は lcsl[s, t] となります。

高得点のポイント

  • アルゴリズムの最終結果がDPテーブルのどの位置に格納されるかを正しく理解していること。
  • 列の長さを表す変数 ss および tt を用いてインデックスを正確に指定していること。

設問3は,正答率が平均的であった。二次元配列lcslの要素番号の誤りが散見された。二次元配列のどの要素をどのように更新するべきかを理解し,アルゴリズムの操作を適切にプログラムとして記述する能力を身につけてほしい。

設問4

本文中の に入れる適切な字句を,sstt を用いて答えよ。

模範解答

st

採点基準(配点 3点)

知識・理解度(内容)(2点)

  • 2: 動的計画法の二重ループ構造から、時間計算量が対象の2つの列の長さの積に比例することを正確に理解し記述できている。
  • 1: 計算量の概念やループ回数に依存することは理解しているが、式の要素にわずかな不備がある。
  • 0: 時間計算量についての理解が不十分であり、誤った計算量を示している。

論理性(構造)(1点)

  • 1: 問題文の指示通り、$s$ と $t$ を用いた数式として論理的に正しく表現されている。
  • 0: $s$ と $t$ 以外の変数を使用しているか、数式としての構造が不適切である。

解説

正解の根拠

動的計画法を用いたこのアルゴリズムの時間計算量を評価します。

アルゴリズムは、長さ ss の列と長さ tt の列に対して二重ループを実行し、各ループ内での操作(要素の比較や加算)は定数時間 O(1)O(1) で行われます。
したがって、全体の処理ステップ数は s×ts \times t に比例するため、時間計算量は O(st)O(st) となります。
空欄 には、オーダー記法の中身である stst が入ります。

高得点のポイント

  • 二重ループの構造から、計算量がループ回数の積になることを正確に理解していること。
  • 問題文の指示通り、変数 sstt を用いて式を正しく表現していること。

設問4は,正答率が平均的であった。効率的なアルゴリズムを選択するために,時間計算量を理解することは重要であるので,理解を深めてほしい。