Валидное Судоку
Описание задачи #

Вам дана доска Судоку размером 9 x 9.
Определите, является ли эта доска валидной.
На валидность проверяются только заполненные ячейки в соответствии со следующими правилами.
- Каждая строка должна содержать цифры от 1 до 9 без повторений.
- Каждый столбец должен содержать цифры от 1 до 9 без повторений.
- Каждый из девяти квадратов доски размером
3 x 3должен содержать цифры от 1 до 9 без повторения.
Доска Судоку (частично заполненная) может быть валидной, но не обязательно решаемой. Только заполненные ячейки должны быть проверены в соответствии с упомянутыми правилами.
Ограничения #
- В доску 9 строк
- В доске 9 столбцов
- В качестве значений используются числа
1 - 9или"."для обозначения пустых строк
Примеры #
-
Входные данные
[ ["5","3",".",".","7",".",".",".","."], ["6",".",".","1","9","5",".",".","."], [".","9","8",".",".",".",".","6","."], ["8",".",".",".","6",".",".",".","3"], ["4",".",".","8",".","3",".",".","1"], ["7",".",".",".","2",".",".",".","6"], [".","6",".",".",".",".","2","8","."], [".",".",".","4","1","9",".",".","5"], [".",".",".",".","8",".",".","7","9"], ]Ответ:
true -
Входные данные
[ ["8","3",".",".","7",".",".",".","."], ["6",".",".","1","9","5",".",".","."], [".","9","8",".",".",".",".","6","."], ["8",".",".",".","6",".",".",".","3"], ["4",".",".","8",".","3",".",".","1"], ["7",".",".",".","2",".",".",".","6"], [".","6",".",".",".",".","2","8","."], [".",".",".","4","1","9",".",".","5"], [".",".",".",".","8",".",".","7","9"], ]Ответ:
false
Решение #
В первую очередь, чтобы решить эту задачу, надо придумать каким образом отслеживать уникальность значений в каждой строке, столбце и квадрате. Для этого мы создадим несколько вспомогательных структур фиксированной длины для отслеживания используемых значений.
Самая сложная часть этой задачи заключается как раз в том, чтобы понять, как должны работать эти структуры.
Для начала определим квадратную матрицу (массив массивов) rows фиксированного размера.
То есть в нашей матрице будет 9 строк по 9 колонок в каждой.
Как мы будем фиксировать наличие цифры в строке?
Например, если в нулевой строке есть цифра 3, то мы установим значение true в rows[1][2] — в качестве индекса i используем индекс строки, а в качестве j значение цифры - 1.
То есть нулевой индекс в нулевой строке фиксирует наличие цифры 1, первый индекс — цифры 2, второй индекс — цифры 3 и так далее.
Точно такой же подход будем использовать для фиксирования наличия значений в столбцах, поэтому создадим матрицу columns размера 9 х 9.
С определением наличия одинаковых цифр в каждом квадрате дела обстоят немного сложнее, но не сильно. Сначала в уме индексируем каждый квадрат в матрице.

Например, координата 1:1 в таком случае будет обозначать средний квадрат, а 2:2 — нижний правый.
Теперь мы заведем Hash Map с названием quads для хранения сведений о встречающихся в квадрате цифрах.
В качестве ключа мы будем использовать строку с координатой, а в качестве значения массив длиною в 9 элементов, такой же как в матрицах rows и columns.
Таким образом если мы хотим указать, что в среднем квадрате, например, находится цифра 2, то нам нужно в мапу под ключом 1:1 создать массив и в этот массив под индексом 1 положить значение равное true.
После того как мы разобрались с этой частью алгоритм становится совсем простым.
Мы обходим все элементы матрицы и проверяем только те, где есть цифры.
Если в матрицах rows, columns и в мапе quads для искомой цифры все значения равны false, значит эта цифра пока что уникальна.
В таком случае устанавливаем все значения в true и переходим на следующую итерации.
Если же хоть в одной из структур будет значение равное true, то это означает что такая цифра уже существует и не является уникальной.
В таком случае мы можем прервать функцию и вернуть ответ false.
Если же мы пройдем по всей матрице и ни разу не встретим значение true, то все элементы уникальны и из функции можно вернуть ответ true.
Кстати, для определения координаты квадрата нам достаточно индекс колонки и строки в матрице поделить на 3 без остатка.
-
package main import ( "fmt" "strconv" ) func isValidSudoku(board [][]byte) bool { rows := [9][9]bool{} columns := [9][9]bool{} quads := make(map[string][9]bool, 9) n := len(board) for row := 0; row < n; row++ { for col := 0; col < n; col++ { if board[row][col] == '.' { continue } strNum := string(board[row][col]) num, _ := strconv.Atoi(strNum) pos := num - 1 key := fmt.Sprintf("%d:%d", row/3, col/3) if rows[row][pos] || columns[col][pos] || quads[key][pos] { return false } rows[row][pos] = true columns[col][pos] = true temp := quads[key] temp[pos] = true quads[key] = temp } } return true } -
export const isValidSudoku = (board: string[][]): boolean => { const n = board.length const rows: boolean[][] = [] const columns: boolean[][] = [] const quads: Record<string, boolean[]> = {} // Предварительно заполняем массивы false значениями for (let i = 0; i < n; i++) { rows.push(new Array(n).fill(false)) columns.push(new Array(n).fill(false)) } for (let row = 0; row < n; row++) { for (let col = 0; col < n; col++) { if (board[row][col] === '.') { continue } const pos = Number(board[row][col]) - 1 const key = `${Math.floor(row / 3)}:${Math.floor(col / 3)}` if (!quads[key]) { quads[key] = new Array(n).fill(false) } if (rows[row][pos] || columns[col][pos] || quads[key][pos]) { return false } rows[row][pos] = true columns[col][pos] = true quads[key][pos] = true } } return true }
Оценка сложности #
По времени
Так как мы перебираем всю матрицу, то сложность перебора будет равна O(n2).
По памяти
n2— для хранения матрицыrows.n2— для хранения матрицыcolumns.n2— для хранения мапыquads.
В сумме мы используем 3 * n2 дополнительной памяти, поэтому итоговая сложность равна O(n2).