貪欲で 札を 連続する 数の 組に分ける
数の
海外で定番の問題リスト 4 本には入っていません
問題
カードゲームで、cards と、size がsize 枚のtrue、false を
- 同じ数の
札が 何枚ある こともあります。 1 つの 組の 中に 同じ数は 入りません cardsの枚数が sizeで割り 切れなければ、 分けられないので falseですsizeが 1 なら、どの 札も 1 枚で 組に なるので trueですcardsの長さは 1 以上 10000 以下、 値は どれも 0 以上 1000000000 以下、 sizeは 1 以上cardsの長さ以下です
入力と出力の例
例 1
[3, 4, 5]、[4, 5, 6]、[10, 11, 12] の 3 組に
例 2
2 を[2, 3] しかなく、4, 4, 5, 6 では 4 が 2 枚とも 5 を
例 3
4 枚は 3 で
面接での聞かれ方
面接では、
- 「手札を、
同じ 枚数の 連番の 組に 分けられますか」 - 「同じ数の
札は 何枚ありますか」と 聞くと、 「何枚でもあります」と 返ってくるのが ふつうです - 書き終えた
あとに 「値の 幅が 小さいと 分かっているなら 速く できますか」と、 続きを 聞かれる こともあります
同じ数の
ヒント
まだ開いていません
付箋は 3 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3答えに近い
アルゴリズムのチートシートの「配列と文字列」に、
関わる学び
配列と
まず 考える手順
段 1
素直に
段 2
いま残っているx をx よりx をx からx, x + 1, ..., x + size - 1 しか
段 3
数ごとのx がc 枚残っていれば、x からc 組まとめてx + 1 から x + size - 1 までのc 枚ずつfalse です。true です。
解答
Python
Python
from collections import Counter
def can_split_straights(cards, size):
if len(cards) % size != 0:
return False
count = Counter(cards)
for x in sorted(count):
c = count[x]
if c == 0:
continue
for y in range(x, x + size):
if count[y] < c:
return False
count[y] -= c
return TrueJavaScript
JavaScript
function canSplitStraights(
cards, size
) {
if (cards.length % size !== 0) {
return false;
}
const count = new Map();
for (const x of cards) {
count.set(
x, (count.get(x) ?? 0) + 1,
);
}
const keys = [...count.keys()];
keys.sort((p, q) => p - q);
for (const x of keys) {
const c = count.get(x);
if (c === 0) {
continue;
}
for (
let y = x;
y < x + size;
y++
) {
const have = count.get(y) ?? 0;
if (have < c) {
return false;
}
count.set(y, have - c);
}
}
return true;
}Go
Go
import "slices"
func canSplitStraights(
cards []int, size int,
) bool {
if len(cards)%size != 0 {
return false
}
count := map[int]int{}
for _, x := range cards {
count[x]++
}
keys := make([]int, 0,
len(count))
for x := range count {
keys = append(keys, x)
}
slices.Sort(keys)
for _, x := range keys {
c := count[x]
if c == 0 {
continue
}
for y := x; y < x+size; y++ {
if count[y] < c {
return false
}
count[y] -= c
}
}
return true
}Go の map を range でCounter は、count[y] を
計算量
- 時間は O(n log n + n × size) です。
異なる 数を 並べ替えるのに O(n log n)、 組を 作る ときに 数ごとに size個の数を 減らします - 使う
記憶は O(n) です。 数ごとの 枚数と、 並べた 数の 一覧を持ちます - 組を
まとめて c組ずつ作るので、同じ数の 札が 多くても、 減らす回数は 異なる 数の 個数 × sizeで済みます
よく ある間違い
- 並べ替えずに
数を見る。 数え上げた 順のまま 見ると、 例 1 では 最初に 出てくる 5 から [5, 6, 7]を作ろうとし、 7 が 無いので、 分けられるのに falseを返します。 最も 小さい 数から 見るから、 迷わずに 組が 決まります - 組を 1
組ずつ作る たびに 並べ直す。 札を 1 枚ずつ 取り出して 並べ直すと、 手間が O(n² log n) まで 増えます。 枚数を 数えておけば、 同じ数の 組を まとめて作れます - 割り
切れるかを 確かめない。 例 3 は 数が 連続していても、 4 枚は 3 で 割り 切れないので 分けられません。 先に 確かめると、 無駄な 数え上げを せずに済みます - 数の
値で 引く 並びを作る。 値の 幅で 長さを 決めた 並びに 枚数を 入れると、 値が 1000000000 まであるので 記憶が 足りません。 枚数は 辞書で 持ち、 出てくる 数だけを 並べ替えます
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「残っている
最も 小さい 数の 札は、 その 数から 始まる 組に しか 入れません。 なので、 数ごとの 枚数を 数えて 小さい 順に 見て、 残っている 枚数の 分だけ、 その 数から size個続く組を まとめて 作ります。 続く 数の どれかが 足りなければ false です。 時間は 並べ替えの O(n log n) と、 組を 作る O(n × size) です。」
書き終えたら、
「例 2 で
たどります。 6 枚は 2 で 割り 切れます。 最も 小さい 2 は 1 枚なので、 [2, 3]を 1 組作り、2 と 3 の 残りは 0 です。 3 は 残りが 0 なので 飛ばします。 4 は 2 枚残っているので [4, 5]を 2 組作ろうとしますが、5 は 1 枚しかないので false を 返します。」