集合で数独の盤に同じ数字の重なりが無いか確かめる

途中まで数字の入った 9 × 9 の数独の盤で、行、列、3 × 3 の箱のどれにも同じ数字が 2 回入っていないかを確かめます。見た数字を「どの行、どの列、どの箱か」と組にして集合に覚える、ハッシュの練習です。

海外で定番の問題リスト 4 本には入っていません

問題

数独の盤を、9 本の文字列の並び board で受け取ります。board[r] は上から r 番目の行で、9 文字のそれぞれが "1" から "9" の数字か、まだ空いているマスを表す "." です。

入っている数字だけを見て、次の 3 つを全部守っていれば true、1 つでも破っていれば false を返す関数を書いてください。

  • どの行にも、同じ数字が 2 回入っていない

  • どの列にも、同じ数字が 2 回入っていない

  • 盤を縦横 3 つずつに割った 9 個の 3 × 3 の箱のどれにも、同じ数字が 2 回入っていない

  • 空いたマスは数えません。空いたマスに数字を入れて最後まで解けるかどうかは問いません

  • board は必ず 9 本で、どの文字列も 9 文字です。文字は "1" から "9" と "." だけです

入力と出力の例

例 1

入っている数字は、どの行、列、箱でも重なっていません。

board = ["1.3..6.8.", ".5.78..2.", "7..1.3.5.", "..45...9.", "5.7.9.2.4", ".9..3.5.7", "3..6.89..", ".7.9..3.5", "9.2..5.7."]
true

例 2

左上の箱に 4 が 2 つあります。行も列も違いますが、箱が同じなので false です。

board = ["4..2.....", "..4...7..", ".........", ".........", ".........", ".........", ".........", ".........", "........."]
false

例 3

例 1 の最後の行の 7 を 8 に替えた盤です。右から 2 番目の列に、いちばん上の行の 8 と合わせて 8 が 2 つ入ります。

board = ["1.3..6.8.", ".5.78..2.", "7..1.3.5.", "..45...9.", "5.7.9.2.4", ".9..3.5.7", "3..6.89..", ".7.9..3.5", "9.2..5.8."]
false

面接での聞かれ方

面接では、盤を見せて短く聞かれることがよくあります。

  • 「この数独の盤が、決まりを破っていないか確かめてください」
  • 「解けるかどうかも見ますか?」と、範囲を自分から確かめるかを見られます
  • 書き終えたあとに「盤を 1 回だけなめて確かめられますか?」と広げられることもあります

確かめるのは、すでに入っている数字の重なりだけです。最後まで解けるかどうかはバックトラックの別の問題なので、書く前に範囲をそろえておきます。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「ハッシュマップ」に、この型の要点を表とコードでまとめています。

ハッシュは、値から番地を計算して、探す手間をほぼ 1 回にする仕組みです。重複や回数の問題は、この形で解けます。

main.py
def valid_sudoku(board: list[str]) -> bool:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(valid_sudoku(["1.3..6.8.", ".5.78..2.", "7..1.3.5.", "..45...9.", "5.7.9.2.4", ".9..3.5.7", "3..6.89..", ".7.9..3.5", "9.2..5.7."]))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します