動的計画法で 文字列から 別の 文字列を 拾い出す方法を数える
文字列 s から、
海外で定番の問題リスト 4 本には入っていません
問題
文字列 s と t をs からt に
- 選ぶ文字は
隣どうしでなくてかまいません。 sの中の 場所の 組が 違えば、 別の 選び方として数えます tがsから作れなければ 0 を返します sとtの長さは 1 以上 1000 以下で、 英小文字だけです - 答えは 2^31 未満に
なる ものとします。 tの前から 何文字かだけを 作る 数え方も、 同じく 2^31 未満に 収まる ものとします
入力と出力の例
例 1
b は 0 番の 1 つです。
例 2
m は 2 番か 3 番、
例 3
s の
面接での聞かれ方
面接では、
- 「
sの部分列で tと等しくなる 物は、 いく つありますか」 - 最初に
「同じ 中身でも、 拾う 場所が 違えば 別に 数えますか?」と 確かめられるかを 見られます - 書き終えると
「表を 1 行分に 減らすとき、 どちら 向きに 埋めますか?」と 聞かれます
最長共通部
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「動的計画法 (DP)」に、
関わる学び
動的計画法は、
まず 考える手順
段 1
s のs の
段 2
s のt のways[i][j] とs の i 文字目をways[i-1][j] 通りです。t の j 文字目とways[i-1][j-1] 通り
段 3
t のways[i][0] はways[len(s)][len(t)] です。
段 4
s のt のs のt の 2 か所に
解答
Python
Python
def count_picks(s, t):
n = len(t)
w = [1] + [0] * n
back = range(n, 0, -1)
for ch in s:
for j in back:
if t[j - 1] != ch:
continue
w[j] += w[j - 1]
return w[n]JavaScript
JavaScript
function countPicks(s, t) {
const n = t.length;
const w = Array(n + 1)
.fill(0);
w[0] = 1;
const back = [];
for (let j = n; j > 0; j--) {
back.push(j);
}
for (const ch of s) {
for (const j of back) {
if (t[j - 1] === ch) {
w[j] += w[j - 1];
}
}
}
return w[n];
}Go
Go
func countPicks(
s, t string,
) int {
n := len(t)
w := make([]int, n+1)
w[0] = 1
for i := 0; i < len(s); i++ {
for j := n; j >= 1; j-- {
if t[j-1] == s[i] {
w[j] += w[j-1]
}
}
}
return w[n]
}w[j] は、ways[i][j] を 1 行にs のt のt の j 文字目とw[j-1] をback のw[j-1] は
計算量
- 時間は O(m × n) です (m は
s、n は tの長さ)。 sの 1 文字ごとにtの長さだけ回ります。 1000 文字どうしなら 100 万回です - 使う
記憶は O(n) です。 持つのは 1 行分の 並びだけです - 2 次元の
表を そのまま 持つ 書き方も 時間は 同じで、 記憶が O(m × n) になります
よく ある間違い
- j を
前から回す。 s = "a"、t = "aa"で、1 つの a を tの 1 文字目と 2 文字目の両方に 使い、 1 を 返してしまいます。 答えは 0 です - 空の
tを 0 通りにする。w[0]を 0 にすると、 どこからも 数が 増えず、 答えが いつも 0 に なります。 何も 選ばない 1 通りから始めます - 同じ
文字の ときに 「使う」だけを数える。 文字が 同じでも、 使わない 選び方は 残ります。 w[j] = w[j-1]と上書きすると、 例 2 で 後ろの t を 選ぶ数え方が消えます
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「
sの前から i 文字で tの前から j 文字を 作る 数を 表にします。 i 文字目を 使わない数は そのまま、 tの j 文字目と同じなら 使う 数として 1 つ左上を 足します。 空の tは 1 通りです。1 行分の 並びで、 同じ 文字を 2 回使わないよう j を 後ろから 回します。 時間は O(m × n)、 記憶は O(n) です。」
書き終えたら、
「例 2 の
committeeとmitでたどります。 並びは 空、 m、 mi、 mit の 順に 1、 0、 0、 0 で 始まります。 2 番と 3 番の m で m の 数が 2、 4 番の i で mi が 2、 5 番の t で mit が 2、 6 番の t で もう 2 足して 4 です。 最後の 2 つの e は tに無いので 変わりません。」