スライディングウィンドウで 必要な 文字を 全部含む 最短の 部分を探す
文字列 s の
海外で定番の問題リスト 4 本には入っていません
問題
文字列 s と t をs のt のt に
- そういう
部分が 無ければ、 空の 文字列 ""を返します - いちばん短い
物が 2 つ以上 ある ときは、 sの中で いちばん左に ある 物を返します - 英字の
大文字と 小文字は 別の 文字として扱います - 部分の
中に、 tに無い 文字や、 余った 文字が 入っていてもかまいません sとtの長さは、 どちらも 1 以上 100000 以下で、 英字の 大文字と 小文字だけで できています
入力と出力の例
例 1
A、"ACB" が
例 2
t には N が 2 つあります。"NAN" です。
例 3
s の B はt の b は
面接での聞かれ方
面接では、
- 「t の
文字を 全部含む、 s の 最小の 窓を 求めてください」 - 「t に
同じ 文字が 何個も ある ときは、 その 数だけ 要りますか?」と、 範囲を 自分から 確かめるかを 見られます - 「同じ
長さが 2 つ あったら、 どちらを 返しますか?」も、 聞いておくと 答えが 1 つに 決まります
全部の
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「配列と文字列」に、
関わる学び
配列と
まず 考える手順
段 1
素直にt の
段 2
窓をt の
段 3
「全部そろったか」をt のneed にmissing でmissing を 1 減らし、missing を 1 増やします。missing が 0 なら
段 4
t にmissing は
解答
Python
Python
from collections import Counter
def min_cover(s, t):
need = Counter(t)
missing = len(t)
lo = 0
start = 0
best_len = len(s) + 1
for r, c in enumerate(s):
if need[c] > 0:
missing -= 1
need[c] -= 1
while missing == 0:
size = r - lo + 1
if size < best_len:
best_len = size
start = lo
out = s[lo]
need[out] += 1
if need[out] > 0:
missing += 1
lo += 1
if best_len > len(s):
return ""
end = start + best_len
return s[start:end]JavaScript
JavaScript
function minCover(
s,
t
) {
const need = {};
for (const c of t) {
need[c] ??= 0;
need[c]++;
}
const n = s.length;
let missing = t.length;
let lo = 0;
let start = 0;
let bestLen = n + 1;
for (let r = 0; r < n; r++) {
const c = s[r];
need[c] ??= 0;
if (need[c] > 0) missing--;
need[c]--;
while (missing === 0) {
const size = r - lo + 1;
if (size < bestLen) {
bestLen = size;
start = lo;
}
const out = s[lo];
need[out]++;
if (need[out] > 0) {
missing++;
}
lo++;
}
}
if (bestLen > n) return "";
const end = start + bestLen;
return s.slice(start, end);
}Go
Go
func minCover(
s string,
t string,
) string {
need := map[byte]int{}
for i := 0; i < len(t); i++ {
need[t[i]]++
}
missing := len(t)
lo, start := 0, 0
bestLen := len(s) + 1
for r := 0; r < len(s); r++ {
c := s[r]
if need[c] > 0 {
missing--
}
need[c]--
for missing == 0 {
size := r - lo + 1
if size < bestLen {
bestLen = size
start = lo
}
out := s[lo]
need[out]++
if need[out] > 0 {
missing++
}
lo++
}
}
if bestLen > len(s) {
return ""
}
end := start + bestLen
return s[start:end]
}need[c] はmissing はbest_len (bestLen) を s より 1 長い
計算量
- 時間は O(n + m) です。
n は sの長さ、 m は tの長さです。 右の 端も 左の 端も、 それぞれ前へ n 回までしか 進みません - 使う
記憶は O(k) です。 k は 文字の 種類の 数で、 英字なら 52 種類までなので O(1) と 言ってもかまいません - 段 1 の
全部の 部分を 調べる 書き方は、 O(n²) 回の 確かめになります
よく ある間違い
- 文字の
種類だけを 数え、 数を 数えない。 例 2 で N を 1 つ 含めば よいと 考えると、 "AN"を返します。 tの同じ 文字は、 その 数だけ要ります missingを、要らない 文字が 入った ときにも減らす。 need[c] > 0のときだけ 減らします。 余った 文字や tに無い 文字で 減らすと、 そろっていないのに そろったと見ます - 縮めるのを
ifで 1 回だけにする。 1 回では最短まで 縮まらず、 例 1 では "ACB"を見落として "CXAYB"を返します。 そろっている あいだは 縮め続けます - 同じ
長さで 答えを 書き換える。 <=で比べると、 同じ 長さの とき右の 物に 替わり、 いちばん左を 返す決まりを破ります - 大文字と
小文字を 同じに見る。 例 3 で "B"を返してしまいます。 決まりを 最初に 確かめます
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「左右の
端を 持つ窓で 解きます。 文字ごとに あと 何個要るかを 表に 持ち、 足りない合計を 1 つの 数で 持ちます。 右の 端を 伸ばし、 足りない 合計が 0 に なったら、 0 の あいだ左の 端を 縮めながら 最短を 記録します。 左右の 端は 前へ 進むだけなので、 時間は O(n + m) です。 同じ 長さの ときは 書き換えないので、 いちばん左の 物が 残ります。」
書き終えたら、
「例 1 で
たどります。 右の 端を 伸ばし、 左から 5 文字目の B で "CXAYB"がそろいます。 左を 縮めると C が 抜けて 足りなくなるので、 ここでは 長さ 5 です。 続けて 伸ばし、 2 つ目の C で 縮めると "XAYBZAC"から"BZAC"まで縮み、 長さ 4。 最後の B で "ACB"まで縮み、 長さ 3 が 最短です。」