動的計画法で 2 つの 文字列を 順を 保って 混ぜると 3 つ目に なるか調べる
2 つの
海外で定番の問題リスト 4 本には入っていません
問題
文字列 a、b、c をa と b のa のb のc が
- 混ぜ方は
自由です。 aから何文字か 続けて 取り、 次に bから何文字か 取る、を どんな 順で 繰り返してもかまいません aとbの文字は 全部使います。 cの長さが aとbの長さの 和と 違えば falseですaとbの長さは 0 以上 100 以下、 cの長さは 0 以上 200 以下で、 英小文字だけです
入力と出力の例
例 1
a の a、b の x、a の b、b の y、a の c のc に
例 2
先頭の a を 2 つとも a からb の ab では ba がa から、b から、a から
例 3
c の 2 文字目と 3 文字目は b が 2 つa と b の b はc の b より
面接での聞かれ方
面接では、
- 「2 つの
文字列を 順を 保って 混ぜると、 3 つ目の 文字列に なりますか」 - 前から
合う方を 取っていく 書き方を 出すと、 「同じ 文字が 両方に あったら?」と 聞かれます - 書き終えると
「表を 1 行分に 減らせますか?」と続きます
例 2 のように、
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「動的計画法 (DP)」に、
関わる学び
動的計画法は、
まず 考える手順
段 1
c のa からb からc の
段 2
前からa から i 文字、b から j 文字をc のok[i][j] と
段 3
ok[i][j] がa からok[i-1][j] がa の i 文字目が c の i + j 文字目とb からok[i][j-1] がb の j 文字目が c の i + j 文字目とok[0][0] は
段 4
i を 1 b のfalse を
解答
Python
Python
def can_interleave(a, b, c):
m, n = len(a), len(b)
if m + n != len(c):
return False
a, b = " " + a, " " + b
ok = [False] * (n + 1)
for i in range(m + 1):
for j in range(n + 1):
if i == j == 0:
ok[j] = True
continue
ch = c[i + j - 1]
up = i > 0
up &= ok[j]
up &= a[i] == ch
left = j > 0
left &= ok[j - 1]
left &= b[j] == ch
ok[j] = up or left
return ok[n]JavaScript
JavaScript
function canInterleave(
a, b, c,
) {
const m = a.length;
const n = b.length;
if (m + n !== c.length) {
return false;
}
const ok = Array(n + 1)
.fill(false);
for (let i = 0; i <= m; i++) {
for (const j of ok.keys()) {
if (i + j === 0) {
ok[j] = true;
continue;
}
const ch = c[i + j - 1];
const up = i > 0
&& ok[j]
&& a[i - 1] === ch;
const left = j > 0
&& ok[j - 1]
&& b[j - 1] === ch;
ok[j] = up || left;
}
}
return ok[n];
}Go
Go
func canInterleave(
a, b, c string,
) bool {
m, n := len(a), len(b)
if m+n != len(c) {
return false
}
ok := make([]bool, n+1)
for i := 0; i <= m; i++ {
for j := 0; j <= n; j++ {
if i+j == 0 {
ok[j] = true
continue
}
ch := c[i+j-1]
up := i > 0 && ok[j] &&
a[i-1] == ch
left := j > 0 &&
ok[j-1] && b[j-1] == ch
ok[j] = up || left
}
}
return ok[n]
}up はa からleft は b からok[j] は 1 つ上のok[i-1][j]、ok[j-1] はok[i][j-1] です。ch は c の i + j 文字目 (0 からb だけからa だけからi > 0、j > 0 でa と b のa[i] が i 文字目になるように&= は、
計算量
- 時間は O(m × n) です (m は
a、n は bの長さ)。 表の 欄を 1 つずつ 埋めます。 100 文字どうしなら 約 1 万欄です - 使う
記憶は O(n) です。 持つのは 1 行分の 並びだけです - 2 次元の
表を そのまま 持つ 書き方も 時間は 同じで、 記憶が O(m × n) になります
よく ある間違い
- 合う方を
欲張りで取る。 両方の 先頭が 同じ 文字の とき、 いつも aから取ると 例 2 で falseを返します。 どちらから 取った 場合も 残すために 表を使います - 長さの
和を 確かめない。 a = "a"、b = ""、c = "aa"では、表が 見るのは cの 1 文字目までなので、右下が 真に なり trueを返してしまいます。 始めに 長さを比べます cの番号を i + j にする。 aから i 文字、bから j 文字を使った ときの 最後の 文字は、 cの i + j - 1 番 (0 から数える ) です。 1 つずれると、 合わない 文字どうしを 比べてしまいます
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「
aから i 文字、bから j 文字を使って cの前から i + j 文字が 作れるかを 表にします。 最後の 1 文字を aから取ったか bから取ったかの 2 通りで、 上か左の 欄が 真で 文字が 合えば 真です。 1 行分の 並びで 足り、 時間は O(m × n)、 記憶は O(n) です。」
書き終えたら、
「例 2 の
aa、ab、aabaでたどります。 aから 1 文字の a で真、 そこから bの a を取って cの aa、bの b を取って aab まで 真です。 最後に aの 2 文字目の a を取ると aaba で、 右下の 欄が 真に なります。」