ウェブエンジニア問題集
第12章

パターン認識トレーニング — 「問題文→解法」の変換を鍛える

7
この章の目次開く

Part 4は実戦演習編です。ここまでに道具は揃いました。残る課題はただ1つ——初見の問題文から、どの道具を使うかを見抜くことです。

上級者と初心者の差は、知識量よりも変換の速さにあります。上級者は問題文を読みながら「制約10^5、"連続する部分列"、"最長"…尺取り法だな」と、キーワードが解法に直結しています。この章はその回路を作るための訓練です。

シグナル→解法 早見表20選

各章末で提示してきた「見分け方」の総集編です。問題文に現れる言葉から引けるように並べ直しました。

#問題文のシグナル第一候補
1N ≤ 20 +「選ぶ/選ばない」bit全探索7
2N ≤ 10 +「並べ方・順序」順列全列挙7
3制約が小さい(10^6回で全部試せる)素直に全探索7
4「ペアが存在するか」「重複」「過去に出たか」Set存在判定4
5「回数」「最頻」「グループ分け」Mapカウント/キー設計4
6「アナグラム」「並べ替えると同じ」ソート文字列をキーに4
7ソート済み +「合計がXのペア」二つのポインタ3
8連続する部分配列の最長/最短/個数」尺取り法3
9「区間の合計」を何度も累積和3
10「括弧の対応」「直近と消し合う」スタック5
11「迷路」「最小手数」(重みなし)BFS6
12「グループはいくつ」「到達できるか」DFS/BFS連結成分6
13「x以上の個数」(ソート可能)lowerBound8
14「〇〇できる最大のL」「最大値の最小化」答えで二分探索8
15「重ならないよう最大数選ぶ」区間スケジューリング貪欲9
16「何通りあるか」数え上げDP10
17「容量W以下で価値最大」ナップサックDP10
18「最長増加/共通部分列」系列DP10
19「最短」+ 辺の重みがバラバラダイクストラ11
20「つなぐ」「合併」+「同じグループ?」Union-Find11
学習者学習者

20個かぁ。これ、暗記するもの?それとも使ってるうちに覚えるもの?

暗記しようとしなくて大丈夫です。使い方は「問題を解く→詰まったらこの表を上から眺める→当てはまりそうな行の章を復習」。この表を「引く」行為そのものが訓練で、10問も解けば頻出行は勝手に頭に入ります。

初見の問題を分解する5ステップ

表に直接ヒットしない問題のために、汎用の思考手順も持っておきましょう。

Step 1  制約を見る
        → 許される計算量が決まる(第2章の表)。これで解法の半分は絞れる

Step 2  問題文を「入力→出力」に要約する
        → 物語を剥がす。「N個の整数から条件を満たす〇〇の最大値」のように1行化

Step 3  全探索ならどう書くか考える
        → 「何を全部試せば答えが出るか」。これが解法の原型(第7章)

Step 4  間に合わないなら、どの道具で削るか
        → 探索が重い→ハッシュ/二分探索、選択の重複→DP、1本道でよさそう→貪欲

Step 5  小さい例で手を動かして検算する
        → 要素3個の入力で自分の解法をシミュレート。反例が出たらStep 4へ戻る
手順を考える人

ケーススタディ: 5ステップを実演する

実際の出題風の問題文で、頭の中を実況します。

問題: あるお店にN人の客が来る。i番目の客の来店時刻はT_i、滞在時間はS_iである。座席は1つしかない。座席が空いていれば客は座り、空いていなければ帰ってしまう。座れる客の数を最大化…ではなく、客が全員座れるように座席をK個用意したい。必要な最小のKを求めよ。(N ≤ 10^5、時刻 ≤ 10^9)

Step 1 制約: N ≤ 10^5 → O(N log N)まで。時刻が10^9 → 時刻を配列添字にはできない(座標圧縮か、しない工夫を)。

Step 2 要約: 「N個の区間 [T_i, T_i + S_i) が与えられる。同時に重なる区間の最大数を求めよ」——物語を剥がすとこうなります。

Step 3 全探索: すべての時刻について重なり数を数える → 時刻が10^9通りで不可能。

Step 4 削る: 重なり数が変化するのは「誰かの来店/退店の瞬間」だけ。イベント(+1/-1)を時刻順にソートして走査すれば O(N log N)。——「区間の重なり」をイベントのソートに変換する、いもす法/イベントソートと呼ばれる考え方です(累積和の親戚です)。

Step 5 検算: 客2人が同時刻に来店したら? 来店イベントを退店イベントより先に処理する順序づけが必要——と、端のケースに気づけます。

function minSeats(customers: [number, number][]): number {
  const events: [number, number][] = [];   // [時刻, +1/-1]
  for (const [t, s] of customers) {
    events.push([t, +1], [t + s, -1]);
  }
  // 時刻順。同時刻は退店(-1)を先に処理(退店と同時の来店は座れる、という仕様なら)
  events.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
 
  let current = 0, best = 0;
  for (const [, delta] of events) {
    current += delta;
    best = Math.max(best, current);
  }
  return best;
}
typescript

初見でも、5ステップに沿えば「知らない問題」が「知っている部品の組み合わせ」に分解できる——この感覚がゴールです。

面接では思考過程を声に出す

採用面接では、この5ステップを声に出すだけでそのまま高評価の回答になります。

「制約からO(N log N)が必要です」(Step 1) → 「問題を整理すると区間の最大重なり数です」(Step 2) → 「全時刻を調べるのは時刻の範囲が広すぎます」(Step 3) → 「変化点だけ見ればよいので、イベントをソートします」(Step 4)

沈黙して完璧な解法を出すより、この実況ができる候補者のほうが「一緒に働ける」と評価されます。練習のときから独り言で実況する癖をつけておくと、本番で自然に出ます。

メンターメンター

パターン認識は「問題を解いた数」ではなく「解法を言語化した数」で伸びます。解けた問題も解けなかった問題も、「シグナルは何だったか」を一言メモに残す。それが20選の表を自分専用に育てていく作業になりますよ。

まとめ

  • 実力差の正体は「問題文→解法」の変換速度。シグナル20選の表を引きながら解いて回路を作る
  • 初見の問題は5ステップ: 制約→要約→全探索→削る→小さい例で検算
  • 物語を剥がして「入力→出力」の1行に要約すると、既知のパターンが見えてくる
  • 解いた後に「シグナルは何だったか」を言語化すると定着が加速する
  • 面接ではこの思考手順を声に出すこと自体が評価になる

次章は本書の卒業試験、模擬テストです。paiza・AtCoder相当の問題セットで、時間を計って腕試ししましょう。